溫馨提示×

溫馨提示×

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

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

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

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

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

場景一:采用函數(shù)引用方式的setTimeout調(diào)用

閉包的一個通常的用法是為一個在某一函數(shù)執(zhí)行前先執(zhí)行的函數(shù)提供參數(shù)。例如,在web環(huán)境中,一個函數(shù)作為setTimeout函數(shù)調(diào)用的第一個參數(shù),是一種很常見的應用。

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

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

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

場景二:將函數(shù)關(guān)聯(lián)到對象的實例方法

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

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

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

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

    /*
    這個構(gòu)造器函數(shù),通過將元素的ID作為字符串參數(shù)傳遞進來,
    來創(chuàng)建將自身關(guān)聯(lián)到DOM元素上的對象,
    對象實例想在對應的元素觸發(fā)onclick、onmouseover、onmouseout事件時
    對應的方法被調(diào)用。
    */
    function DhtmlObject(elementId) {
      /*
      調(diào)用一個方法來獲得一個DOM元素的引用
      如果沒有找到,則為null
      */
      var el = getElementWith(elementId);
      /*
      因為if語句塊,el變量的值在內(nèi)部進行了類型轉(zhuǎn)換,變成了boolean類型
      所以當它指向一個對象,結(jié)果就為true,如果為null則為false
      */
      if (el) {
        /*
        為了給元素指定一個事件處理函數(shù),調(diào)用了associateObjWithEvent函數(shù),
        利用它自己(this關(guān)鍵字)作為被調(diào)用方法的對象,并且提供方法名稱
        */
        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的實例都能夠?qū)⑺鼈冏陨黻P(guān)聯(lián)到它們感興趣的DOM元素上去,不需要去擔心這些元素將被其他的代碼怎么處理,以及被全局命名空間“污染”或者與其他的DhtmlObject的實例產(chǎn)生沖突。

場景三:封裝相關(guān)的功能集

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

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

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

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

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

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

/*
     定義一個全局變量:getImgInPositionedDivHtml
     被賦予對外部函數(shù)表達式一次調(diào)用返回的一個內(nèi)部函數(shù)表達式

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

      空字符串僅僅被用來作為一個占位符,它將被內(nèi)部函數(shù)的參數(shù)代替
      */
      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>'
      ];

      /*
      返回一個內(nèi)部函數(shù)對象,他是函數(shù)表達式執(zhí)行返回的結(jié)果
      */
      return (function (url, id, width, height, top, left, altText) {
        /*
        分配各種參數(shù)給對應的數(shù)組元素
        */
        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;

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

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

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

向AI問一下細節(jié)

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

js
AI