成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

變量對(duì)象+作用域鏈+閉包

ygyooo / 3304人閱讀

摘要:傳入值現(xiàn)在結(jié)果是正確的了創(chuàng)建了一個(gè)匿名函數(shù),通過把變量作為參數(shù)傳進(jìn)去,這樣在執(zhí)行的時(shí)候,由于內(nèi)部的形參能夠訪問到變量,所以無需到父級(jí)作用域鏈上進(jìn)行尋找,因此最后輸出達(dá)到預(yù)期目的。

下文根據(jù)湯姆大叔的深入javascript系列文章刪改,如果想深入理解請(qǐng)閱讀湯姆大叔的系列文章。
http://www.cnblogs.com/TomXu/...

變量對(duì)象 初步介紹
變量對(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

相關(guān)文章

  • 前端基礎(chǔ)進(jìn)階(四):詳細(xì)圖解作用域鏈閉包

    摘要:之前一篇文章我們?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...

    aikin 評(píng)論0 收藏0
  • 談js中的作用域鏈閉包

    摘要:所以,當(dāng)在函數(shù)中使用全局變量的時(shí)候,所產(chǎn)生的代價(jià)是最大的,因?yàn)槿謱?duì)象一直處于作用域鏈的最末位置,讀取局部變量是最快的。 什么是作用域 在編程語言中,作用域控制著變量與參數(shù)的可見性及生命周期,它能減少名稱沖突,而且提供了自動(dòng)內(nèi)存管理(javascript 語言精粹) 靜態(tài)作用域 再者,js不像其他的編程語言一樣,擁有著塊級(jí)作用域,就像下面一段代碼。 function afunction...

    LucasTwilight 評(píng)論0 收藏0
  • 還是不明白JavaScript - 執(zhí)行環(huán)境、作用域、作用域鏈閉包嗎?

    摘要:所以,全局執(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)。之前我也看過...

    jlanglang 評(píng)論0 收藏0
  • [ JS 進(jìn)階 ] 閉包,作用域鏈,垃圾回收,內(nèi)存泄露

    摘要:執(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)...

    clasnake 評(píng)論0 收藏0
  • JS基礎(chǔ)知識(shí):變量對(duì)象、作用域鏈閉包

    摘要:前言這段時(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í)上造成了一些困惑,...

    Keven 評(píng)論0 收藏0
  • 溫故js系列(14)-閉包&垃圾回收&內(nèi)存泄露&閉包應(yīng)用&作用域鏈&

    摘要:該對(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...

    Amio 評(píng)論0 收藏0

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

0條評(píng)論

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