国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

深入理解閉包的概念

anyway / 1349人閱讀

摘要:離開(kāi)閉包的泥淖,給這個(gè)例子一個(gè)較為合理的寫(xiě)法總結(jié)理解閉包的概念是重要的,但我們不應(yīng)當(dāng)過(guò)多的使用閉包,它有優(yōu)點(diǎn),也優(yōu)缺點(diǎn),是一把雙刃劍。

閉包

關(guān)于閉包,目前有如下說(shuō)法:

閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合(MDN)

函數(shù)對(duì)象可以通過(guò)作用域鏈相互關(guān)聯(lián)起來(lái),函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi)。這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中被稱(chēng)為閉包(JavaScript權(quán)威指南)

閉包,指的是詞法表示包括不被計(jì)算的變量的函數(shù),也就是說(shuō),函數(shù)可以使用函數(shù)之外定義的變量(W3school)

閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)(JavaScript高級(jí)程序設(shè)計(jì))

根據(jù)排列順序也可以看出,我個(gè)人對(duì)這些說(shuō)法的認(rèn)同程度。其實(shí)大家說(shuō)的都是同一個(gè)東西,只是描述是否精確的問(wèn)題。
為了充分理解以上的說(shuō)法,要先理解一些術(shù)語(yǔ):

詞法作用域

簡(jiǎn)單來(lái)說(shuō),詞法作用域就是:根據(jù)變量定義時(shí)所處的位置,來(lái)確定變量的作用范圍。(詞法解析,通過(guò)閱讀包含變量定義在內(nèi)的數(shù)行源碼就能知道變量的作用域)
舉例而言,定義在全局的變量,它的作用范圍是全局的,所以被稱(chēng)為全局變量;定義在函數(shù)內(nèi)部的變量,它的作用范圍是局部的,所以被稱(chēng)為局部變量。

作用域鏈

函數(shù)在創(chuàng)建時(shí),會(huì)同時(shí)保存它的作用域鏈。——這個(gè)保存的作用域鏈包含了該函數(shù)所處的作用域?qū)ο蟮募稀R驗(yàn)樗泻瘮?shù)都在全局作用域下聲明,所以這個(gè)保存的作用域鏈一定包含全局作用域?qū)ο螅╣lobal)。此外,如果函數(shù)是在其他函數(shù)內(nèi)部聲明的,那它保存的作用域鏈中除了global之外,還包含它創(chuàng)建時(shí)所處的局部作用域?qū)ο蟆#ㄔ赾hrome中直接標(biāo)識(shí)為closure,在firefox中則標(biāo)識(shí)為塊)。顯然,這個(gè)作用域鏈實(shí)際上是一個(gè)指向作用域?qū)ο蠹系闹羔樍斜?/strong>。

函數(shù)在執(zhí)行時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境、執(zhí)行時(shí)作用域鏈以及活動(dòng)對(duì)象。——活動(dòng)對(duì)象(activation object)是指當(dāng)前作用域?qū)ο?處于活動(dòng)狀態(tài)的,它包含arguments、this以及所有局部變量)。執(zhí)行時(shí)作用域鏈實(shí)際上是函數(shù)創(chuàng)建時(shí)保存的作用域鏈的一個(gè)復(fù)制,但它更長(zhǎng),因?yàn)榛顒?dòng)對(duì)象被推入了執(zhí)行時(shí)作用域鏈的前端。每次函數(shù)在執(zhí)行時(shí)都會(huì)創(chuàng)建一個(gè)新的執(zhí)行環(huán)境(execution context),它對(duì)應(yīng)著一個(gè)全新的執(zhí)行時(shí)作用域鏈。

根據(jù)JavaScript的垃圾回收機(jī)制:一般情況下,函數(shù)在執(zhí)行完畢后,執(zhí)行環(huán)境(包括執(zhí)行時(shí)作用域鏈)將自動(dòng)被銷(xiāo)毀,占用的內(nèi)存將被釋放。

垃圾回收機(jī)制

JavaScript 是一門(mén)具有自動(dòng)垃圾回收機(jī)制的語(yǔ)言。
這種機(jī)制的原理是找出那些不再繼續(xù)使用的變量,然后釋放其占用的內(nèi)存。目前,找出不再繼續(xù)使用的變量的策略有兩種:標(biāo)記清除(主流瀏覽器)和引用計(jì)數(shù)(IE8及以下)。
標(biāo)記清除:垃圾收集器在運(yùn)行的時(shí)候會(huì)給存儲(chǔ)在內(nèi)存中的所有變量都加上標(biāo)記;然后,它會(huì)去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記;最后,垃圾收集器銷(xiāo)毀那些帶標(biāo)記的值并回收它們所占用的內(nèi)存空間。垃圾收集器會(huì)按照固定的時(shí)間間隔周期性地執(zhí)行這一操作。
引用計(jì)數(shù):當(dāng)聲明了一個(gè)變量并將一個(gè)引用類(lèi)型值賦給該變量時(shí),則這個(gè)值的引用次數(shù)就是 1。如果同一個(gè)值又被賦給另一個(gè)變量,則該值的引用次數(shù)加 1。相反,如果包含對(duì)這個(gè)值引用的變量又取得了另外一個(gè)值,則這個(gè)值的引用次數(shù)減 1。當(dāng)這個(gè)值的引用次數(shù)變成 0 時(shí),則說(shuō)明沒(méi)有辦法再訪問(wèn)這個(gè)值了,因而就可以將其占用的內(nèi)存空間回收回來(lái)。這樣,當(dāng)垃圾收集器下次再運(yùn)行時(shí),它就會(huì)釋放那些引用次數(shù)為零的值所占用的內(nèi)存。(引用計(jì)數(shù)的失敗之處在于它無(wú)法處理循環(huán)引用)

現(xiàn)在,什么是閉包呢?
——“閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合”(MDN)

function a(){
  console.log("1");
}
a();

以上例子:函數(shù)a,和它創(chuàng)建時(shí)所在的全局作用域,構(gòu)成一個(gè)閉包。于是有人說(shuō)每個(gè)函數(shù)實(shí)際上都是一個(gè)閉包,但準(zhǔn)確來(lái)講,應(yīng)該是每個(gè)函數(shù)和它創(chuàng)建時(shí)所處的作用域構(gòu)成一個(gè)閉包。
但這個(gè)閉包叫什么名字呢?
在chrome和firefox調(diào)試中,將函數(shù)a所在作用域的名字,作為閉包的名字;在JavaScript高級(jí)程序設(shè)計(jì)中則將函數(shù)a的名字,作為閉包的名字。這樣一來(lái),每個(gè)函數(shù)都是一個(gè)閉包的說(shuō)法似乎又“準(zhǔn)確”了一些。
其實(shí)我們書(shū)寫(xiě)的所有js代碼,都處在全局作用域這個(gè)大大的閉包之中,只是我們意識(shí)不到它作為一個(gè)閉包存在著。

function a(){
  var b = 1;
  function c(){
    console.log(b);
  }
  return c
}
var d = a();
d(); // 1

以上例子:除了函數(shù)a和全局作用域構(gòu)成一個(gè)閉包以外,函數(shù)c和局部作用域(函數(shù)a的作用域)也構(gòu)成一個(gè)閉包。
先不關(guān)注這些函數(shù)內(nèi)部的邏輯,我們只看結(jié)構(gòu):
函數(shù)a聲明了,然后在var d = a();這一句執(zhí)行。通過(guò)以上對(duì)詞法作用域、作用域鏈以及垃圾回收機(jī)制的理解,我們可以得出以下結(jié)論:
函數(shù)a在聲明時(shí)保存了一個(gè)作用域鏈,在它執(zhí)行時(shí)又創(chuàng)建了一個(gè)執(zhí)行環(huán)境(以及執(zhí)行時(shí)作用域鏈)。一般情況下,當(dāng)函數(shù)a執(zhí)行完畢,它的執(zhí)行環(huán)境將被銷(xiāo)毀。但在這個(gè)例子里,函數(shù)a中的變量c,被return突破作用域的限制賦值給了變量d,而變量c是一個(gè)函數(shù),它使用了它創(chuàng)建時(shí)所處的作用域(函數(shù)a的作用域)中的變量b,這意味著,在函數(shù)d執(zhí)行完畢之前,函數(shù)c以及它創(chuàng)建時(shí)所處的作用域中變量(變量b)不可以被銷(xiāo)毀。
這打斷了函數(shù)a執(zhí)行環(huán)境的銷(xiāo)毀進(jìn)程,它被保存了下來(lái),以備函數(shù)d調(diào)用時(shí)使用。看看被保存的是什么?一個(gè)函數(shù)c和它創(chuàng)建時(shí)所在的作用域。一個(gè)閉包。

function a(){
  var b = 1;
  function c(){
    b++; console.log(b);
  }
  return c
}
var d = a();
d(); // 2
d(); // 3
var e = a();
e(); // 2
e(); // 3

以上例子,函數(shù)a被執(zhí)行了兩次并分別賦值給了d、e,顯然,函數(shù)a的兩次執(zhí)行創(chuàng)建了兩個(gè)執(zhí)行環(huán)境,它們本該被銷(xiāo)毀,但由于函數(shù)c的存在(有權(quán)訪問(wèn)另一個(gè)函數(shù)內(nèi)部變量的函數(shù)),它們被保存下來(lái)。函數(shù)d的兩次執(zhí)行,使用同一個(gè)執(zhí)行環(huán)境中的變量b,所以b遞增了;由于函數(shù)e使用的是另一個(gè)執(zhí)行環(huán)境中的變量b,所以它重新開(kāi)始遞增。

所以,什么是閉包呢?
閉包是一個(gè)函數(shù)和它創(chuàng)建時(shí)所在作用域的組合。在我們?nèi)粘?yīng)用中,通常是將一個(gè)函數(shù)定義在另一個(gè)函數(shù)的內(nèi)部并從中返回,以使它成為一個(gè)在函數(shù)外部仍有權(quán)限訪問(wèn)函數(shù)內(nèi)部作用域的函數(shù)。
jQuery就是定義在一個(gè)匿名自執(zhí)行函數(shù)內(nèi)部的函數(shù),當(dāng)它被賦值給全局作用域變量$jQuery時(shí),在全局作用域使用$jQuery方法,就能夠訪問(wèn)到那個(gè)匿名自執(zhí)行函數(shù)的內(nèi)部作用域(其中包含的變量等)。在jQuery這個(gè)例子中,內(nèi)部函數(shù)jQuery和其所在的匿名自執(zhí)行函數(shù)作用域就構(gòu)成一個(gè)閉包。

一個(gè)經(jīng)典的例子:

// html 
var lis = document.querySelector("ul").children; for (var i = 0; i < lis.length; i++) { lis[i].addEventListener("click", function(){ console.log(i); }) } var event = document.createEvent("MouseEvent"); event.initEvent("click", false, false); for (var j = 0; j < lis.length; j++) { lis[j].dispatchEvent(event); }

為頁(yè)面上的所有l(wèi)i標(biāo)簽綁定點(diǎn)擊函數(shù),點(diǎn)擊后輸出自身的序號(hào)。在以上例子中,顯然將輸出 3, 3, 3;而非 0, 1, 2;
一個(gè)通俗的解釋是,當(dāng)點(diǎn)擊li標(biāo)簽時(shí),for循環(huán)已經(jīng)執(zhí)行完畢,i的值已經(jīng)確定。所以三個(gè)li標(biāo)簽點(diǎn)擊輸出同一個(gè)i的值。
我們稍微改動(dòng)一下代碼:

// html 
var lis = document.querySelector("ul").children; for (var i = 0; i < lis.length; i++) { (function(i){ lis[i].addEventListener("click", function(){ console.log(i); }) })(i); } var event = document.createEvent("MouseEvent"); event.initEvent("click", false, false); for (var j = 0; j < lis.length; j++) { lis[j].dispatchEvent(event); }

以上例子,當(dāng)點(diǎn)擊li標(biāo)簽時(shí),for循環(huán)已經(jīng)執(zhí)行完畢,i的值已經(jīng)確定,可為什么結(jié)果會(huì)輸出 0, 1, 2 呢?
實(shí)際上,這是閉包在作怪:
  click事件的匿名函數(shù) 跟外層自執(zhí)行匿名函數(shù)的作用域構(gòu)成了一個(gè)閉包。在循環(huán)中,外層匿名自執(zhí)行函數(shù)本該在執(zhí)行結(jié)束后銷(xiāo)毀它的執(zhí)行環(huán)境,釋放其內(nèi)存,但由于它的參數(shù)(變量)i 還被事件監(jiān)聽(tīng)函數(shù)引用著,所以這個(gè)執(zhí)行環(huán)境無(wú)法被銷(xiāo)毀,它將被保存著。每一次的循環(huán),匿名自執(zhí)行函數(shù)都將執(zhí)行一次,并保存一個(gè)執(zhí)行環(huán)境;當(dāng)循環(huán)結(jié)束,類(lèi)似的執(zhí)行環(huán)境共有三個(gè),每一個(gè)里面的變量i的值都是不同的。
  回到第一個(gè)例子,匿名事件函數(shù)實(shí)際上和聲明它的全局作用域也構(gòu)成了一個(gè)閉包,但在三次循環(huán)中,i 都未曾離開(kāi)這個(gè)閉包,它一直遞增直至3,三個(gè)點(diǎn)擊事件函數(shù)引用同一個(gè)執(zhí)行環(huán)境中的變量i,它們的值必然是相同的。

離開(kāi)閉包的泥淖,給這個(gè)例子一個(gè)較為合理的寫(xiě)法:

// html 
var lis = document.querySelector("ul").children; var say = function(){ console.log(this.index); } for (var i = 0; i < lis.length; i++) { lis[i].index = i; lis[i].addEventListener("click", say); } var event = document.createEvent("MouseEvent"); event.initEvent("click", false, false); for (var j = 0; j < lis.length; j++) { lis[j].dispatchEvent(event); }

總結(jié):理解閉包的概念是重要的,但我們不應(yīng)當(dāng)過(guò)多的使用閉包,它有優(yōu)點(diǎn),也優(yōu)缺點(diǎn),是一把雙刃劍。使用閉包可以創(chuàng)建一個(gè)封閉的環(huán)境,使得我們可以保存私有變量,避免全局作用域命名沖突,加強(qiáng)了封裝性;但它常駐內(nèi)存的特性也對(duì)網(wǎng)頁(yè)的性能造成了比較大的影響,在引用計(jì)數(shù)的垃圾回收策略下更容易造成內(nèi)存泄漏。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/100191.html

相關(guān)文章

  • JavaScript中閉包

    摘要:閉包引起的內(nèi)存泄漏總結(jié)從理論的角度將由于作用域鏈的特性中所有函數(shù)都是閉包但是從應(yīng)用的角度來(lái)說(shuō)只有當(dāng)函數(shù)以返回值返回或者當(dāng)函數(shù)以參數(shù)形式使用或者當(dāng)函數(shù)中自由變量在函數(shù)外被引用時(shí)才能成為明確意義上的閉包。 文章同步到github js的閉包概念幾乎是任何面試官都會(huì)問(wèn)的問(wèn)題,最近把閉包這塊的概念梳理了一下,記錄成以下文章。 什么是閉包 我先列出一些官方及經(jīng)典書(shū)籍等書(shū)中給出的概念,這些概念雖然...

    HmyBmny 評(píng)論0 收藏0
  • 說(shuō)說(shuō)Python中閉包 - Closure

    摘要:閉包可以用來(lái)在一個(gè)函數(shù)與一組私有變量之間創(chuàng)建關(guān)聯(lián)關(guān)系。夾帶私貨外部變量返回的是函數(shù),帶私貨的函數(shù)支持將函數(shù)當(dāng)成對(duì)象使用的編程語(yǔ)言,一般都支持閉包。所以說(shuō)當(dāng)你的裝飾器需要自定義參數(shù)時(shí),一般都會(huì)形成閉包。 Python中的閉包不是一個(gè)一說(shuō)就能明白的概念,但是隨著你往學(xué)習(xí)的深入,無(wú)論如何你都需要去了解這么一個(gè)東西。 閉包的概念 我們嘗試從概念上去理解一下閉包。 在一些語(yǔ)言中,在函數(shù)中可以(嵌...

    leon 評(píng)論0 收藏0
  • 深入貫徹閉包思想,全面理解JS閉包形成過(guò)程

    摘要:下面我們就羅列閉包的幾個(gè)常見(jiàn)問(wèn)題,從回答問(wèn)題的角度來(lái)理解和定義你們心中的閉包。函數(shù)可以通過(guò)作用域鏈相互關(guān)聯(lián)起來(lái),函數(shù)內(nèi)部的變量可以保存在其他函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱(chēng)為閉包。 寫(xiě)這篇文章之前,我對(duì)閉包的概念及原理模糊不清,一直以來(lái)都是以通俗的外層函數(shù)包裹內(nèi)層....來(lái)欺騙自己。并沒(méi)有說(shuō)這種說(shuō)法的對(duì)與錯(cuò),我只是不想擁有從眾心理或者也可以說(shuō)如果我們說(shuō)出更好更低層的東西,逼格...

    snowell 評(píng)論0 收藏0
  • 深入理解JavaScript(二):由一道題來(lái)思考閉包

    摘要:中所有的事件綁定都是異步編程當(dāng)前這件事件沒(méi)有徹底完成,不再等待,繼續(xù)執(zhí)行下面的任務(wù)當(dāng)綁定事件后,不需要等待執(zhí)行,繼續(xù)執(zhí)行下一個(gè)循環(huán)任務(wù),所以當(dāng)我們點(diǎn)擊執(zhí)行方法的時(shí)候,循環(huán)早已結(jié)束即是最后。 概念 閉包就是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù) 點(diǎn)擊li標(biāo)簽彈出對(duì)應(yīng)數(shù)字 0 1...

    曹金海 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<