中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JS閉包可被利用的常見場景有哪些

發布時間:2021-07-26 09:33:40 來源:億速云 閱讀:128 作者:小新 欄目:web開發

這篇文章給大家分享的是有關JS閉包可被利用的常見場景有哪些的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

場景一:采用函數引用方式的setTimeout調用

閉包的一個通常的用法是為一個在某一函數執行前先執行的函數提供參數。例如,在web環境中,一個函數作為setTimeout函數調用的第一個參數,是一種很常見的應用。

setTimeout將要執行的函數(或者一段JavaScript代碼,但這不是我們要討論的情況)作為它的第一個參數,下一個參數是需要延遲執行的時間。如果一段代碼想通過setTimeout來調用,那么它需要傳遞一個函數對象的引用來作為第一個參數。延遲的毫秒數作為第二個參數,但這個函數對象的引用無法為將要被延遲執行的對象提供參數。
但是,可以調用另一個函數來返回一個內部函數的調用,將那個內部函數對象的引用傳遞給setTimeout函數。內部函數執行時需要的參數,在調用外部函數時傳遞給它。setTimeout在執行內部函數時無需傳遞參數,因為內部函數仍然能夠訪問外部函數調用時提供的參數:

function callLater(paramA, paramB, paramC) {
      /*使用函數表達式創建并放回一個匿名內部函數的引用*/
      return (function () {
        /*
        這個內部函數將被setTimeout函數執行;
        并且當它被執行時,
        它能夠訪問并操作外部函數傳遞過來的參數
        */
        paramA[paramB] = paramC;
      });
    }

    /*
    調用這個函數將在它的執行上下文中創建,并最終返回內部函數對象的引用
    傳遞過來的參數,內部函數在最終被執行時,將使用外部函數的參數
    返回的引用被賦予了一個變量
    */
    var funcRef = callLater(elStyle, "display", "none");
    /*調用setTimeout函數,傳遞內部函數的引用作為第一個參數*/
    hideMenu = setTimeout(funcRef, 500);

場景二:將函數關聯到對象的實例方法

有很多這樣的場景:需要分配一個函數對象的引用,以便在未來的某個時間執行該函數。那么閉包對于為這個將要執行的函數提供引用會非常有幫助。因為該函數可能直到執行時才能夠被訪問。

有一個例子就是,一個javascript對象被封裝用來參與一個特定DOM元素的交互。它有doOnClick、doMouseOver以及doMouseOut方法。并且想在DOM元素上對應的事件被觸發時執行這些方法。但是,可能會有關聯著DOM元素的任意數量的javascript對象被創建,并且單個的實例并不知道那些實例化它們的代碼將如何處理它們。對象實例不知道怎樣去“全局”地引用它們自己,因為它們不知道哪個全局變量(如果存在)的引用將被分配給它們。

所以,問題是執行一個與特定javascript對象實例關聯的事件處理函數,并且知道調用那個對象的哪個方法。
接下來的一個例子,在有元素事件處理的對象實例的關聯函數上使用一個簡單的閉包。通過傳遞event對象以及要關聯元素的一個引用,為事件處理器分配不同的對象實例方法以供調用。

/*
    一個給對象實例關聯一個事件處理器的普通方法,
    返回的內部函數被作為事件的處理器,
    對象實例被作為obj參數,對象上將要被調用的方法名稱被作為第二個參數
    */
    function associateObjWithEvent(obj, methodName) {
      /*返回的內部函數被用來作為一個DOM元素的事件處理器*/
      return (function (e) {
        /*
        事件對象在DOM標準的瀏覽器中將被轉換為e參數,
        如果沒有傳遞參數給事件處理內部函數,將統一處理成IE的事件對象
        */
        e = e || window.event;
        /*
        事件處理器調用obj對象上的以methodName字符串標識的方法
        并傳遞兩個對象:通用的事件對象,事件處理器被訂閱的元素的引用
        這里this參數能夠使用,因為內部函數已經被執行作為事件處理器所在元素的一個方法
        */
        return obj[methodName](e, this);
      });
    }

    /*
    這個構造器函數,通過將元素的ID作為字符串參數傳遞進來,
    來創建將自身關聯到DOM元素上的對象,
    對象實例想在對應的元素觸發onclick、onmouseover、onmouseout事件時
    對應的方法被調用。
    */
    function DhtmlObject(elementId) {
      /*
      調用一個方法來獲得一個DOM元素的引用
      如果沒有找到,則為null
      */
      var el = getElementWith(elementId);
      /*
      因為if語句塊,el變量的值在內部進行了類型轉換,變成了boolean類型
      所以當它指向一個對象,結果就為true,如果為null則為false
      */
      if (el) {
        /*
        為了給元素指定一個事件處理函數,調用了associateObjWithEvent函數,
        利用它自己(this關鍵字)作為被調用方法的對象,并且提供方法名稱
        */
        el.onclick = associateObjWithEvent(this, "doOnClick");
        el.onmouseover = associateObjWithEvent(this, "doOnMouseOver");
        el.onmouseout = associateObjWithEvent(this, "doOnMouseOut");
      }
    }

    DhtmlObject.prototype.doOnClick = function (event, element) {
      //doOnClick body
    }
    DhtmlObject.prototype.doMouseOver = function (event, element) {
      //doMouseOver body
    }

    DhtmlObject.prototype.doMouseOut = function (event, element) {
      //doMouseOut body
    }

任何DhtmlObject的實例都能夠將它們自身關聯到它們感興趣的DOM元素上去,不需要去擔心這些元素將被其他的代碼怎么處理,以及被全局命名空間“污染”或者與其他的DhtmlObject的實例產生沖突。

場景三:封裝相關的功能集

閉包可以創建額外的scope,這可以被用來組合相關的或有依賴性的代碼。用這種方式可以最大限度地減少代碼干擾的危害。假設,一個函數被用來創建一個字符串并且避免重復串聯的操作(比如創建一系列的中間字符串)。一個想法是,用一個數組來順序存儲字符串的一部分,然后使用Array.prototype.join方法輸出結果(使用一個空字符串作為它的參數)。數組將扮演著輸出緩沖區的角色,但局部定義它又將會導致它在函數的每次執行時再次創建。如果這個數組只是作為唯一的變量被分配給每一個函數調用,這將會有點小題大做。

一個解決方案是將數組提升為全局變量,讓它不需要被再次創建也能夠再次使用。但結果并不是想的那么簡單,另外,一個全局變量關聯這使用緩沖數組的函數,那將會有第二個全局屬性(函數本身也是window對象的屬性)關聯這個數組,這將讓代碼失去一定的可控性。因為如果將它使用在其他地方。這段代碼的創建者不得不記住包含函數的定義以及數組的定義邏輯。它也使得代碼不那么容易與其他代碼整合,因為將從原來只需要確定函數名是否在全局命名空間中唯一,變成有必要確定和該函數關聯的數組的名稱是否在全局命名空間中保持唯一。

一個閉包可以讓緩沖數組關聯(干凈地包含)它依賴的函數,并且同時保持緩沖數組的屬性名稱,像被分配在全局空間中一樣,同時能夠避免名稱沖突以及代碼交互干擾的危險。

這里有一招就是通過執行一個內聯的函數表達式創建一個額外的執行上下文,讓那個函數表達式返回一個內聯的函數,該函數被外部代碼使用。緩沖數組被定義在內聯執行的函數表達式中,作為一個局部變量。它僅被調用一次,所以該數組只被創建一次。但是對于依賴它的函數來說該數組是一直可訪問的,并且可被重用的。

接一下的代碼創建了一個函數,將返回一個HTML字符串,其中的一部分是不變的,但那些不變的字符串需要被穿插進作為參數傳遞進來的變量中。

一個內聯執行的函數表達式返回了內部函數對象的一個引用。并且分配了一個全局變量,讓它可以被作為一個全局函數來調用。而緩沖數組作為一個局部變量被定義在外部函數表達式中。它沒有被擴展到全局命名空間中,并且無論函數什么時候使用它都不需要被再次創建。

/*
     定義一個全局變量:getImgInPositionedDivHtml
     被賦予對外部函數表達式一次調用返回的一個內部函數表達式

     內部函數返回了一個HTML字符串,代表一個絕對定位的DIV
     包裹這一個IMG元素,而所有的變量值都被作為函數調用的參數
    */
    var getImgInPositionedDivHtml = (function () {
      /*
      buffAr 數組被定義在外部函數表達式中,作為一個局部變量
      它只被創建一次。數組的唯一實例對內部函數是可見的,
      所以它可以被用于每一次的內部函數執行

      空字符串僅僅被用來作為一個占位符,它將被內部函數的參數代替
      */
      var buffAr = [
         '<div id="',
        '',  //index 1, DIV ID attribute
        '" style="position:absolute;top:',
        '',  //index 3, DIV top position
        'px;left:',
        '',  //index 5, DIV left position
        'px;width:',
        '',  //index 7, DIV width
        'px;height:',
        '',  //index 9, DIV height
        'px;overflow:hidden;\"><img src=\"',
        '',  //index 11, IMG URL
        '\" width=\"',
        '',  //index 13, IMG width
        '\" height=\"',
        '',  //index 15, IMG height
        '\" alt=\"',
        '',  //index 17, IMG alt text
        '\"><\/div>'
      ];

      /*
      返回一個內部函數對象,他是函數表達式執行返回的結果
      */
      return (function (url, id, width, height, top, left, altText) {
        /*
        分配各種參數給對應的數組元素
        */
        buffAr[1] = id;
        buffAr[3] = top;
        buffAr[5] = left;
        buffAr[13] = (buffAr[7] = width);
        buffAr[15] = (buffAr[9] = height);
        buffAr[11] = url;
        buffAr[17] = altText;

        /*
        返回連接每個元素后創建的字符串
        */
        return buffAr.join('');
      });
    })();

如果一個函數依賴另一個或幾個函數,但那些其他的函數并不期望與任何其他的代碼產生交互。那么這個簡單的技巧(使用一個對外公開的函數來擴展那些函數)就可以被用來組織那些函數。

感謝各位的閱讀!關于“JS閉包可被利用的常見場景有哪些”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

js
AI

西宁市| 同德县| 嘉兴市| 玉门市| 墨脱县| 许昌县| 扎鲁特旗| 固安县| 格尔木市| 兰溪市| 泸定县| 上饶县| 东乡族自治县| 宁南县| 黄大仙区| 昌乐县| 深泽县| 潮州市| 桐梓县| 甘德县| 澄江县| 舟曲县| 镇安县| 永靖县| 璧山县| 灵台县| 宁明县| 凌云县| 松阳县| 顺义区| 白水县| 双鸭山市| 凌海市| 根河市| 霸州市| 谢通门县| 万州区| 广灵县| 岑巩县| 鄂托克旗| 石屏县|