国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

JS閉包可被利用的常見場景小結

2019-11-19 16:52:40
字體:
來源:轉載
供稿:網友

場景一:采用函數引用方式的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('');      });    })();

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 辛集市| 荆州市| 吉隆县| 淮阳县| 西丰县| 霸州市| 微山县| 宜川县| 静安区| 来安县| 永兴县| 富宁县| 江孜县| 深水埗区| 镇沅| 湟中县| 清水县| 文山县| 辽源市| 南投县| 鄂州市| 万全县| 白山市| 呼伦贝尔市| 贡山| 通化县| 苏尼特右旗| 永寿县| 商城县| 灯塔市| 广州市| 长丰县| 阿坝县| 山西省| 涿州市| 北流市| 吴忠市| 应城市| 江陵县| 沾化县| 开封县|