摘要:但在作用域鏈中,外部函數(shù)的活動(dòng)對(duì)象始終處于第二位,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象始終處于第三位,直至作為作用域鏈重點(diǎn)的全局執(zhí)行環(huán)境。仍然引用中的函數(shù)在執(zhí)行完畢后,其活動(dòng)對(duì)象也不會(huì)被銷(xiāo)毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對(duì)象。
前言:最近在細(xì)讀Javascript高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書(shū)中很多地方翻譯的差強(qiáng)人意,所以用自己所理解的,嘗試解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝您的指出。文中絕大部分內(nèi)容引用自《JavaScript高級(jí)程序設(shè)計(jì)第三版》。
閉包是指有權(quán)訪問(wèn)另一個(gè)作用域中的變量的函數(shù)。
創(chuàng)建閉包的常見(jiàn)方式, 就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另外一個(gè)函數(shù)。
function createComparisonFunction(propertyName) { return function(object1, object2) { var value1 = object1[propertyName]; // 注意, return出的匿名函數(shù)訪問(wèn)了外部函數(shù)中的變量propertyname var value2 = object2[propertyName]; // 注意, return出的匿名函數(shù)訪問(wèn)了外部函數(shù)中的變量propertyname if(value1 < value2) { return -1; } else if (value > value2) { return 1; } else { return 0; } } }
在這個(gè)例子中,突出的那兩行代碼是內(nèi)部函數(shù)(一個(gè)匿名函數(shù))中的代碼,這兩行代碼訪問(wèn)了外部函數(shù)中的變量propertyName。
即使這個(gè)內(nèi)部函數(shù)被返回了,而且是在其他地方被調(diào)用了,但它仍然可以訪問(wèn)變量propertyName。
之所以還能夠訪問(wèn)這個(gè)變量,是因?yàn)閮?nèi)部函數(shù)的作用域鏈中包含createComparisonFunction()的作用域。
當(dāng)某個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建有一個(gè)執(zhí)行環(huán)境(execution context)以及相應(yīng)的作用域鏈。
然后,使用arguments和其他命名參數(shù)的值來(lái)初始化函數(shù)的活動(dòng)對(duì)象(activation object)。
但在作用域鏈中,外部函數(shù)的活動(dòng)對(duì)象始終處于第二位,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象始終處于第三位,...直至作為作用域鏈重點(diǎn)的全局執(zhí)行環(huán)境。
在函數(shù)執(zhí)行過(guò)程中,為讀取和寫(xiě)入變量的值,就需要在作用鏈中查找變量。
function compare(value1, value2) { if(value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } var result = compare(5,10);
以上代碼先定義了compare()函數(shù),然后又在全局作用中調(diào)用了它。
當(dāng)調(diào)用compare()時(shí),會(huì)創(chuàng)建一個(gè)包含arguments、value1和value2的活動(dòng)對(duì)象。
全局執(zhí)行環(huán)境的變量對(duì)象(包含result和compare)在compare()執(zhí)行環(huán)境的作用鏈中處于第二位。
function compare(value1, value2) { if(value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } var result = compare(5,10); //作用域鏈偽代碼 /* compare[[scope]] = { global variable: { result, compare }, local variable: { arguments: [5, 10], value1 : 5, value2: 10 } } */
后臺(tái)的每個(gè)執(zhí)行環(huán)境都有一個(gè)表示變量的對(duì)象——變量對(duì)象。
全局環(huán)境的變量對(duì)象始終存在,而compare()函數(shù)這樣的局部環(huán)境的變量對(duì)象,則只在函數(shù)執(zhí)行的過(guò)程中存在。
在創(chuàng)建compare()函數(shù)時(shí),會(huì)創(chuàng)建一個(gè)預(yù)先包含全局變量對(duì)象的作用域鏈,這個(gè)作用域鏈被保存在內(nèi)部的[[Scope]]屬性中。
當(dāng)調(diào)用compare()函數(shù)時(shí),會(huì)為函數(shù)創(chuàng)建一個(gè)執(zhí)行環(huán)境,然后通過(guò)復(fù)制函數(shù)的[[Scope]]屬性中的對(duì)象構(gòu)建起執(zhí)行環(huán)境的作用域鏈。此后,本地活動(dòng)對(duì)象(也可以說(shuō)是函數(shù)執(zhí)行時(shí),內(nèi)部的變量)被創(chuàng)建并被推入執(zhí)行環(huán)境作用域的前端。
對(duì)于函數(shù)的執(zhí)行環(huán)境而言,其作用域鏈中包含兩個(gè)變量對(duì)象:
1. 本地活動(dòng)對(duì)象。
2. 全局變量對(duì)象。
作用域鏈本質(zhì)上是一個(gè)指向變量對(duì)象的指針列表,它只引用但不包含變量對(duì)象。
無(wú)論什么時(shí)候在函數(shù)中訪問(wèn)一個(gè)變量時(shí),就會(huì)從作用域鏈中搜索具有相應(yīng)名字的變量。一般來(lái)講,當(dāng)函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷(xiāo)毀,內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的變量對(duì)象)。
但是,閉包的情況又有所不同。
在另一個(gè)函數(shù)內(nèi)部定義的函數(shù)會(huì)將包含函數(shù)(即外部函數(shù))的活動(dòng)對(duì)象添加到它的作用鏈中。
因此, 在createComparisonFunction()函數(shù)內(nèi)部定義的匿名函數(shù)的作用域鏈中,實(shí)際上將會(huì)包含外部函數(shù)createComparisonFunction()的活動(dòng)對(duì)象。
function createComparisonFunction(propertyName) { return function(object1, object2) { var value1 = object1[propertyName]; var value2 = object2[propertyName]; if(value1 < value2) { return -1; } else if(value1 > value2) { return 1; } else { return 0; } } } // 注意調(diào)用createComparisonFunction的返回值仍然是一個(gè)函數(shù)。 var compare = createComparisonFunction("name"); /* createComparisonFunction[[Scope]] = { global Variable: { createComparisionFunction }, local Variable: { arguments: ["name"], propertyName: "name" } } */ var result = compare({name: "Nicholas"}, {name:"Greg"}); /* compare[[Scope]] = { global Variable: { createComparisonFunction }, local Variable: { arguments: [{name: "Nicholas"}, {name:"Greg"}], value1: arguments[0][propertyName], value2: arguments[1][propertyName] } } //propertyName仍然引用createComparisonFunction中的propertyName */
createComparisonFuncion()函數(shù)在執(zhí)行完畢后,其活動(dòng)對(duì)象也不會(huì)被銷(xiāo)毀,因?yàn)槟涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對(duì)象。
換句話說(shuō), 當(dāng)createComparisonFunction()函數(shù)返回后, 其執(zhí)行環(huán)境的作用域鏈會(huì)被銷(xiāo)毀,但它的活動(dòng)對(duì)象仍然會(huì)留在內(nèi)存中;直到匿名函數(shù)被銷(xiāo)毀后,createComparisonFunction()的活動(dòng)對(duì)象想象才會(huì)銷(xiāo)毀。
//創(chuàng)建函數(shù) var compareNames = createComparisonFunction("name"); //調(diào)用函數(shù) var result = compareNames({name: "Nicolas"}, { name: "Greg"}); //接觸對(duì)匿名函數(shù)的引用(以便釋放內(nèi)存),當(dāng)然如果你就是需要使用該變量,那么就不用釋放了,也要看情況的。 compareNames = null;
由于閉包會(huì)攜帶包含它的函數(shù)的作用域,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。過(guò)度使用閉包可能會(huì)導(dǎo)致內(nèi)存占用過(guò)多,因此建議只在絕對(duì)必要時(shí)再考慮使用閉包。雖然像V8等優(yōu)化后的JavaScript引擎會(huì)嘗試回收被閉包占用的內(nèi)存,但是還是要慎重的使用閉包。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/98765.html
摘要:深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。定義對(duì)閉包的定義為閉包是指那些能夠訪問(wèn)自由變量的函數(shù)。 JavaScript深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。 定義 MDN 對(duì)閉包的定義為: 閉包是指那些能夠訪問(wèn)自由變量的函數(shù)。 那什么是自由變量呢? 自由變量是指在函數(shù)中使用的,但既不是函數(shù)參數(shù)也...
摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個(gè)值,這引起的一個(gè)副作用就是如果內(nèi)部函數(shù)在一個(gè)循環(huán)中,那么變量的值始終為最后一個(gè)值。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了...
摘要:使用上一篇文章的例子來(lái)說(shuō)明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問(wèn)外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進(jìn)擊之詞法作用域與作用域鏈?zhǔn)裁词情]包閉包的含義就是閉合,包起來(lái),簡(jiǎn)單的來(lái)說(shuō),就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。在中函數(shù)構(gòu)成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進(jìn)擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來(lái),簡(jiǎn)單的來(lái)說(shuō),就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是...
摘要:當(dāng)面試中讓我解釋一下閉包時(shí)我懵逼了。這個(gè)解釋開(kāi)始可能有點(diǎn)晦澀,讓我們抽絲剝繭摘下閉包的真面目。此文不詳述作用域有專(zhuān)門(mén)的主題闡述,不過(guò)作用域是理解閉包原理的基礎(chǔ)。這才是閉包的真正便利之處。閉包使用不當(dāng)就會(huì)很坑。 原文鏈接 為什么深度學(xué)習(xí)JavaScript? JavaScript如今是最流行的編程語(yǔ)言之一。它運(yùn)行在瀏覽器、服務(wù)器、移動(dòng)設(shè)備、桌面應(yīng)用,也可能包括冰箱。無(wú)需我舉其他再多不相干...
摘要:閉包確實(shí)是一個(gè)說(shuō)爛了的概念,校招社招都會(huì)被問(wèn)到,今天總結(jié)一番。先下定義,閉包是函數(shù)和該函數(shù)的詞法作用域的組合。在這個(gè)栗子里,函數(shù)以及它對(duì)變量的引用就構(gòu)成了閉包。閉包和作用域?qū)τ陂]包和作用域的關(guān)系,我的理解是閉包其實(shí)就是作用域的延伸。 閉包確實(shí)是一個(gè)說(shuō)爛了的概念,校招社招都會(huì)被問(wèn)到,今天總結(jié)一番。先下定義,閉包是函數(shù)和該函數(shù)的詞法作用域的組合。其實(shí)這個(gè)定義是比較教條的,可以直白的理解為閉...
閱讀 2659·2021-09-26 10:13
閱讀 6195·2021-09-08 10:46
閱讀 754·2019-08-30 15:53
閱讀 3027·2019-08-29 16:13
閱讀 2814·2019-08-26 12:23
閱讀 3541·2019-08-26 11:24
閱讀 1189·2019-08-23 18:09
閱讀 1086·2019-08-23 17:08