摘要:傳入值現(xiàn)在結(jié)果是正確的了創(chuàng)建了一個(gè)匿名函數(shù),通過把變量作為參數(shù)傳進(jìn)去,這樣在執(zhí)行的時(shí)候,由于內(nèi)部的形參能夠訪問到變量,所以無需到父級(jí)作用域鏈上進(jìn)行尋找,因此最后輸出達(dá)到預(yù)期目的。
變量對(duì)象 初步介紹下文根據(jù)湯姆大叔的深入javascript系列文章刪改,如果想深入理解請(qǐng)閱讀湯姆大叔的系列文章。
http://www.cnblogs.com/TomXu/...
變量對(duì)象(縮寫為VO)是一個(gè)與執(zhí)行上下文相關(guān)的特殊對(duì)象,它存儲(chǔ)著在上下文中聲明的以下內(nèi)容: 變量 (var, 變量聲明); 函數(shù)聲明 (FunctionDeclaration, 縮寫為FD); 函數(shù)的形參
我們可以用普通的ECMAScript對(duì)象來表示一個(gè)變量對(duì)象:
VO = {};
VO是執(zhí)行上下文的屬性(property),所以:
activeExecutionContext = { VO: { // 上下文數(shù)據(jù)(var, FD, function arguments) } };
只有全局上下文的變量對(duì)象允許通過VO的屬性名稱來間接訪問(因?yàn)樵谌稚舷挛睦?,全局?duì)象自身就是變量對(duì)象),在其它上下文中是不能直接訪問VO對(duì)象的,因?yàn)樗皇莾?nèi)部機(jī)制的一個(gè)實(shí)現(xiàn)。
全局上下文中的變量對(duì)象只有全局上下文的變量對(duì)象允許通過VO的屬性名稱來間接訪問
在全局上下文中,有
VO(globalContext) === global;
因?yàn)槲覀冊(cè)谌稚舷挛闹新暶鞯淖兞康榷际谴嬖谌值淖兞繉?duì)象中,而在全局上下文中的全局變量對(duì)象又是全局對(duì)象本身。所以我們可以通過VO的屬性名稱間接訪問
var a = new String("test"); alert(a); // 直接訪問,在VO(globalContext)里找到:"test" alert(window["a"]); // 間接通過global訪問:global === VO(globalContext): "test" alert(a === this.a); // true var aKey = "a"; alert(window[aKey]); // 間接通過動(dòng)態(tài)屬性名稱訪問:"test"函數(shù)上下文中的變量對(duì)象
在函數(shù)執(zhí)行上下文中,VO是不能直接訪問的,此時(shí)由活動(dòng)對(duì)象(activation object,縮寫為AO)扮演VO的角色。
VO(functionContext) === AO;
在理解函數(shù)上下文中的變量對(duì)象時(shí),我們通過處理上下文代碼的2個(gè)階段來進(jìn)行理解
1.進(jìn)入執(zhí)行上下文 2.執(zhí)行代碼進(jìn)入執(zhí)行上下文
進(jìn)入執(zhí)行上文文的時(shí)候,也即是代碼執(zhí)行之前,此時(shí)VO包含了下列屬性
函數(shù)形參 函數(shù)聲明 變量聲明
其中,函數(shù)聲明的等級(jí)最高,然后是函數(shù)形參,最后才是變量聲明。越高等級(jí)的聲明可以覆蓋低等級(jí)的聲明。
執(zhí)行代碼這個(gè)周期內(nèi),AO/VO已經(jīng)擁有了屬性(不過,并不是所有的屬性都有值,大部分屬性的值還是系統(tǒng)默認(rèn)的初始值undefined )。這個(gè)時(shí)候會(huì)進(jìn)行賦值操作以及執(zhí)行代碼。
alert(x); // function var x = 10; alert(x); // 10 x = 20; function x() {}; alert(x); // 20
在進(jìn)入上下文階段,由于函數(shù)具有最高的級(jí)別,所以第一次alert(x)輸出的是函數(shù)。之后進(jìn)行變量賦值,分別alert 10 20。
function bar (x){ alert(x); var x = 2; } bar(3); //3
由于形參聲明比變量聲明級(jí)別高,所以alert(3),因?yàn)樵谶M(jìn)入執(zhí)行上下文時(shí)變量無法覆蓋形參聲明,所以輸出的是3而不是undefined。
不使用var可以聲明一個(gè)全局變量,這句話是錯(cuò)誤的。alert(a); // undefined alert(b); // "b" 沒有聲明,報(bào)錯(cuò) b = 10; var a = 20;作用域鏈
函數(shù)上下文的作用域鏈在函數(shù)調(diào)用時(shí)創(chuàng)建的,包含活動(dòng)對(duì)象和這個(gè)函數(shù)內(nèi)部的[[scope]]屬性。函數(shù)上下文包括以下內(nèi)容:
activeExecutionContext = { VO: {...}, // or AO this: thisValue, Scope: [ // Scope chain // 所有變量對(duì)象的列表 // for identifiers lookup ] };
其scope定義如下:
Scope = AO + [[Scope]]
[[scope]]是所有父變量對(duì)象的層級(jí)鏈,處于當(dāng)前函數(shù)上下文之上,在函數(shù)創(chuàng)建時(shí)存于其中。
注意這重要的一點(diǎn)--[[scope]]在函數(shù)創(chuàng)建時(shí)被存儲(chǔ)--靜態(tài)(不變的),永遠(yuǎn)永遠(yuǎn),直至函數(shù)銷毀。即:函數(shù)可以永不調(diào)用,但[[scope]]屬性已經(jīng)寫入,并存儲(chǔ)在函數(shù)對(duì)象中。
另外一個(gè)需要考慮的是--與作用域鏈對(duì)比,[[scope]]是函數(shù)的一個(gè)屬性而不是上下文。
因此我個(gè)人的理解是作用域鏈應(yīng)該是函數(shù)本身的活動(dòng)對(duì)象+父級(jí)的變量對(duì)象。其中函數(shù)本身的活動(dòng)對(duì)象總是排在第一位,在尋找標(biāo)識(shí)符的時(shí)候,如果在當(dāng)前活動(dòng)對(duì)象找不到,那么會(huì)遍歷作用域鏈上的父級(jí)變量對(duì)象。其中[[scope]]在函數(shù)創(chuàng)建時(shí)被存儲(chǔ),與函數(shù)共存亡。
var x = 10; function foo() { alert(x); } (function () { var x = 20; foo(); // 10, but not 20 })();
說明函數(shù)的作用域鏈在函數(shù)創(chuàng)建的時(shí)候就已經(jīng)定義好了,是靜態(tài)的,不因?yàn)檎{(diào)用的時(shí)候而改變。
閉包 作用域鏈的加深理解var firstClosure; var secondClosure; function foo() { var x = 1; firstClosure = function () { return ++x; }; secondClosure = function () { return --x; }; x = 2; // 影響 AO["x"], 在2個(gè)閉包公有的[[Scope]]中 alert(firstClosure()); // 3, 通過第一個(gè)閉包的[[Scope]] } foo(); alert(firstClosure()); // 4 alert(secondClosure()); // 3
firstClosure和secondClosure兩個(gè)函數(shù)創(chuàng)建的時(shí)候,內(nèi)部的變量x都是從父級(jí)函數(shù)foo的變量對(duì)象x中引用,所以其實(shí)兩個(gè)函數(shù)都是共享一個(gè)作用域,因此導(dǎo)致x變量共通了。
經(jīng)典閉包var data = []; for (var k = 0; k < 3; k++) { data[k] = function () { alert(k); }; } data[0](); // 3, 而不是0 data[1](); // 3, 而不是1 data[2](); // 3, 而不是2
解釋跟上面類似。function在創(chuàng)建的時(shí)候,內(nèi)部的變量k通過訪問作用域鏈即是父級(jí)的變量對(duì)象k拿到,而當(dāng)函數(shù)被調(diào)用的時(shí)候,for循環(huán)早已執(zhí)行完畢,此時(shí)的K是3,所以三個(gè)函數(shù)調(diào)用的時(shí)候輸出的值都為3。
var data = []; for (var k = 0; k < 3; k++) { data[k] = (function _helper(x) { return function () { alert(x); }; })(k); // 傳入"k"值 } // 現(xiàn)在結(jié)果是正確的了 data[0](); // 0 data[1](); // 1 data[2](); // 2
創(chuàng)建了一個(gè)匿名函數(shù),通過把k變量作為參數(shù)傳進(jìn)去,這樣在執(zhí)行function的時(shí)候,由于內(nèi)部的形參能夠訪問到k變量,所以無需到父級(jí)作用域鏈上進(jìn)行尋找,因此最后輸出達(dá)到預(yù)期目的。
閉包的理論定義這里說明一下,開發(fā)人員經(jīng)常錯(cuò)誤將閉包簡化理解成從父上下文中返回內(nèi)部函數(shù),甚至理解成只有匿名函數(shù)才能是閉包。
ECMAScript中,閉包指的是:
1.從理論角度:所有的函數(shù)。因?yàn)樗鼈兌荚趧?chuàng)建的時(shí)候就將上層上下文的數(shù)據(jù)保存起來了。哪怕是簡單的全局變量也是如此,因?yàn)楹瘮?shù)中訪問全局變量就相當(dāng)于是在訪問自由變量,這個(gè)時(shí)候使用最外層的作用域。 2.從實(shí)踐角度:以下函數(shù)才算是閉包: 1.即使創(chuàng)建它的上下文已經(jīng)銷毀,它仍然存在(比如,內(nèi)部函數(shù)從父函數(shù)中返回) 2.在代碼中引用了自由變量
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/50530.html
摘要:之前一篇文章我們?cè)敿?xì)說明了變量對(duì)象,而這里,我們將詳細(xì)說明作用域鏈。而的作用域鏈,則同時(shí)包含了這三個(gè)變量對(duì)象,所以的執(zhí)行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當(dāng)前的函數(shù)調(diào)用棧,為當(dāng)前正在被執(zhí)行的函數(shù)的作用域鏈,為當(dāng)前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學(xué)JavaScrip...
摘要:所以,當(dāng)在函數(shù)中使用全局變量的時(shí)候,所產(chǎn)生的代價(jià)是最大的,因?yàn)槿謱?duì)象一直處于作用域鏈的最末位置,讀取局部變量是最快的。 什么是作用域 在編程語言中,作用域控制著變量與參數(shù)的可見性及生命周期,它能減少名稱沖突,而且提供了自動(dòng)內(nèi)存管理(javascript 語言精粹) 靜態(tài)作用域 再者,js不像其他的編程語言一樣,擁有著塊級(jí)作用域,就像下面一段代碼。 function afunction...
摘要:所以,全局執(zhí)行環(huán)境的變量對(duì)象始終都是作用域鏈中的最后一個(gè)對(duì)象。講到這里,可能你已經(jīng)對(duì)執(zhí)行環(huán)境執(zhí)行環(huán)境對(duì)象變量對(duì)象作用域作用域鏈的理解已經(jīng)他們之間的關(guān)系有了一個(gè)較清晰的認(rèn)識(shí)。 JavaScript中的執(zhí)行環(huán)境、作用域、作用域鏈、閉包一直是一個(gè)非常有意思的話題,很多博主和大神都分享過相關(guān)的文章。這些知識(shí)點(diǎn)不僅比較抽象,不易理解,更重要的是與這些知識(shí)點(diǎn)相關(guān)的問題在面試中高頻出現(xiàn)。之前我也看過...
摘要:執(zhí)行返回的內(nèi)部函數(shù),依然能訪問變量輸出閉包中的作用域鏈理解作用域鏈對(duì)理解閉包也很有幫助。早期的版本里采用是計(jì)數(shù)的垃圾回收機(jī)制,閉包導(dǎo)致內(nèi)存泄露的一個(gè)原因就是這個(gè)算法的一個(gè)缺陷。 關(guān)于閉包,我翻了幾遍書,看了幾遍視頻,查了一些資料,可是還是迷迷糊糊的,干脆自己動(dòng)手來個(gè)總結(jié)吧 !歡迎指正... (~ o ~)~zZ 1. 什么是閉包? 來看一些關(guān)于閉包的定義: 閉包是指有權(quán)...
摘要:前言這段時(shí)間一直在消化作用域鏈和閉包的相關(guān)知識(shí)。而作用域鏈則是這套規(guī)則這套規(guī)則的具體運(yùn)行。是變量對(duì)象的縮寫那這樣放有什么好處呢我們知道作用域鏈保證了當(dāng)前執(zhí)行環(huán)境對(duì)符合訪問權(quán)限的變量和函數(shù)的有序訪問。 前言:這段時(shí)間一直在消化作用域鏈和閉包的相關(guān)知識(shí)。之前看《JS高程》和一些技術(shù)博客,對(duì)于這些概念的論述多多少少不太清楚或者不太完整,包括一些大神的技術(shù)文章。這也給我的學(xué)習(xí)上造成了一些困惑,...
摘要:該對(duì)象包含了函數(shù)的所有局部變量命名參數(shù)參數(shù)集合以及,然后此對(duì)象會(huì)被推入作用域鏈的前端。如果整個(gè)作用域鏈上都無法找到,則返回。此時(shí)的作用域鏈包含了兩個(gè)對(duì)象的活動(dòng)對(duì)象和對(duì)象。 前端學(xué)習(xí):教程&開發(fā)模塊化/規(guī)范化/工程化/優(yōu)化&工具/調(diào)試&值得關(guān)注的博客/Git&面試-前端資源匯總 歡迎提issues斧正:閉包 JavaScript-閉包 閉包(closure)是一個(gè)讓人又愛又恨的somet...
閱讀 3752·2023-04-25 18:41
閱讀 1187·2021-11-11 16:55
閱讀 1844·2021-09-22 15:54
閱讀 3076·2021-09-22 15:51
閱讀 3554·2019-08-30 15:55
閱讀 1949·2019-08-30 14:19
閱讀 1294·2019-08-29 10:57
閱讀 1711·2019-08-29 10:56