JS入門難點(diǎn)解析6-作用域鏈

(注1:如果有問(wèn)題歡迎留言探討芹助,一起學(xué)習(xí)洪囤!轉(zhuǎn)載請(qǐng)注明出處跑慕,喜歡可以點(diǎn)個(gè)贊哦M蛎蟆)
(注2:更多內(nèi)容請(qǐng)查看我的目錄。)

1. 簡(jiǎn)介

JS入門難點(diǎn)解析5-變量對(duì)象中提到相赁,對(duì)于每個(gè)執(zhí)行上下文相寇,都有三個(gè)重要屬性:

  • 變量對(duì)象(Variable object,VO)
  • 作用域鏈(Scope chain)
  • this
    這篇文章主要講解作用域鏈钮科。

2. 作用域鏈

來(lái)看《JavaScript高級(jí)程序設(shè)計(jì)》里對(duì)作用域鏈的一段解釋:

當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí)唤衫,會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈(scope chain)。作用域鏈的用途绵脯,是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問(wèn)的所有變量和函數(shù)的有序訪問(wèn)佳励。作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象蛆挫。如果這個(gè)環(huán)境是函數(shù)赃承,則將其活動(dòng)對(duì)象(active object)作為變量對(duì)象°睬郑活動(dòng)對(duì)象在最開(kāi)始時(shí)只包含一個(gè)變量瞧剖,即arguments對(duì)象(這個(gè)對(duì)象在全局環(huán)境中是不存在的)。作用域鏈中的下一個(gè)變量對(duì)象來(lái)自包含(外部)環(huán)境,而再下一個(gè)變量來(lái)自下一個(gè)包含環(huán)境抓于。這樣做粤,一直延續(xù)到全局執(zhí)行環(huán)境;全局環(huán)境的變量對(duì)象始終都是作用域鏈中的最后一個(gè)對(duì)象捉撮。

標(biāo)識(shí)符解析是沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符的過(guò)程怕品。搜索過(guò)程始終從作用域鏈的前端開(kāi)始,然后逐級(jí)地向后回溯巾遭,直至找到標(biāo)識(shí)符為止(如果找不到標(biāo)識(shí)符肉康,通常會(huì)導(dǎo)致錯(cuò)誤發(fā)生)。

就是說(shuō)灼舍,作用域鏈吼和,是由當(dāng)前環(huán)境與上層環(huán)境的一系列變量對(duì)象組成,它保證了當(dāng)前執(zhí)行環(huán)境對(duì)符合訪問(wèn)權(quán)限的變量和函數(shù)的有序訪問(wèn)片仿。

3. [[scope]]與函數(shù)創(chuàng)建

函數(shù)的[[scope]]屬性是所有父變量對(duì)象的層級(jí)鏈纹安,在函數(shù)創(chuàng)建時(shí)(函數(shù)生命周期分為函數(shù)創(chuàng)建和函數(shù)調(diào)用階段)存于其中。函數(shù)能訪問(wèn)更高一層上下文的變量對(duì)象,這種機(jī)制是通過(guò)函數(shù)內(nèi)部的[[scope]]屬性來(lái)實(shí)現(xiàn)的砂豌。

注意重要的一點(diǎn)——[[scope]]在函數(shù)創(chuàng)建時(shí)被存儲(chǔ)——靜態(tài)(不變的)厢岂,永遠(yuǎn)永遠(yuǎn),直至函數(shù)銷毀阳距。即:函數(shù)可以永不調(diào)用塔粒,但[[scope]]屬性已經(jīng)寫入,并存儲(chǔ)在函數(shù)對(duì)象中筐摘。由于是靜態(tài)存儲(chǔ)卒茬,再配合上內(nèi)部函數(shù)的[[scope]]屬性是所有父變量的層級(jí)鏈,就導(dǎo)致了閉包的存在咖熟。如下所示:

var a = 10;
function foo() {
    alert(a);
}
  
(function () {
    var a = 20;
    foo(); // 10,這里會(huì)訪問(wèn)foo中的[[scope]]的VO中的a
})();

這個(gè)例子也清晰的表明圃酵,一個(gè)函數(shù)(這個(gè)例子中為從函數(shù)“foo”返回的匿名函數(shù))的[[scope]]持續(xù)存在,即使是在函數(shù)創(chuàng)建的作用域已經(jīng)完成之后馍管。

這也就是前面我們所說(shuō)郭赐,函數(shù)的作用域在函數(shù)定義的時(shí)候就決定了。這是因?yàn)楹瘮?shù)有一個(gè)內(nèi)部屬性 [[scope]]确沸,當(dāng)函數(shù)創(chuàng)建的時(shí)候捌锭,就會(huì)保存所有父變量對(duì)象到其中,你可以理解 [[scope]] 就是所有父變量對(duì)象的層級(jí)鏈罗捎,但是注意:[[scope]] 并不代表完整的作用域鏈观谦!

舉個(gè)例子:

function foo() {
    function bar() {
        ...
    }
}

函數(shù)創(chuàng)建時(shí),各自的[[scope]]為:

foo.[[scope]] = [
    globalContext.VO
];

bar.[[scope]] = [
    fooContext.AO,
    globalContext.VO
];

4. 函數(shù)激活

當(dāng)函數(shù)激活時(shí)桨菜,進(jìn)入函數(shù)上下文豁状,創(chuàng)建 VO/AO 后捉偏,就會(huì)將活動(dòng)對(duì)象添加到作用鏈的前端。
這時(shí)候執(zhí)行上下文的作用域鏈替蔬,我們命名為 Scope:

Scope = [AO].concat([scope]]);

至此告私,作用域鏈創(chuàng)建完畢。

5. 實(shí)例講解

以下面的例子為例承桥,結(jié)合著之前講的變量對(duì)象和執(zhí)行上下文棧,我們來(lái)總結(jié)一下函數(shù)執(zhí)行上下文中作用域鏈和變量對(duì)象的創(chuàng)建過(guò)程:

var scope = 'global scope';
function checkscope(){
    var scope2 = 'local scope';
    return scope2;
}
checkscope();

執(zhí)行過(guò)程如下:

1.checkscope 函數(shù)被創(chuàng)建根悼,保存作用域鏈到內(nèi)部屬性[[scope]]

checkscope.[[scope]] = [
    globalContext.VO
];

2.執(zhí)行 checkscope 函數(shù)凶异,創(chuàng)建 checkscope 函數(shù)執(zhí)行上下文,checkscope 函數(shù)執(zhí)行上下文被壓入執(zhí)行上下文棧

ECStack = [
    checkscopeContext,
    globalContext
];

3.checkscope 函數(shù)并不立刻執(zhí)行挤巡,開(kāi)始做準(zhǔn)備工作剩彬,第一步:復(fù)制函數(shù)[[scope]]屬性創(chuàng)建作用域鏈

checkscopeContext = {
    Scope: checkscope.[[scope]],
}

4.第二步:用 arguments 創(chuàng)建活動(dòng)對(duì)象,隨后初始化活動(dòng)對(duì)象矿卑,加入形參喉恋、函數(shù)聲明、變量聲明

checkscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        scope2: undefined
    }母廷,
    Scope: checkscope.[[scope]],
}

5.第三步:將活動(dòng)對(duì)象壓入 checkscope 作用域鏈頂端

checkscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        scope2: undefined
    },
    Scope: [AO, [[Scope]]]
}

6.準(zhǔn)備工作做完轻黑,開(kāi)始執(zhí)行函數(shù),隨著函數(shù)的執(zhí)行琴昆,修改 AO 的屬性值

checkscopeContext = {
    AO: {
        arguments: {
            length: 0
        },
        scope2: 'local scope'
    },
    Scope: [AO, [[Scope]]]
}

7.查找到 scope2 的值氓鄙,返回后函數(shù)執(zhí)行完畢,函數(shù)上下文從執(zhí)行上下文棧中彈出

ECStack = [
    globalContext
];

參考

JavaScript深入之作用域鏈
前端基礎(chǔ)進(jìn)階(四):詳細(xì)圖解作用域鏈與閉包
JS入門難點(diǎn)解析5-變量對(duì)象
javascript中的[[scope]],scope chain,execution context!
js 中的活動(dòng)對(duì)象 與 變量對(duì)象 什么區(qū)別业舍?
BOOK-《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抖拦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舷暮,更是在濱河造成了極大的恐慌态罪,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件下面,死亡現(xiàn)場(chǎng)離奇詭異复颈,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)诸狭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門券膀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人驯遇,你說(shuō)我怎么就攤上這事芹彬。” “怎么了叉庐?”我有些...
    開(kāi)封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵舒帮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)玩郊,這世上最難降的妖魔是什么肢执? 我笑而不...
    開(kāi)封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮译红,結(jié)果婚禮上预茄,老公的妹妹穿的比我還像新娘。我一直安慰自己侦厚,他們只是感情好耻陕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著刨沦,像睡著了一般诗宣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上想诅,一...
    開(kāi)封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天召庞,我揣著相機(jī)與錄音,去河邊找鬼来破。 笑死篮灼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讳癌。 我是一名探鬼主播穿稳,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼晌坤!你這毒婦竟也來(lái)了逢艘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骤菠,失蹤者是張志新(化名)和其女友劉穎它改,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體商乎,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡央拖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鹉戚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鲜戒。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抹凳,靈堂內(nèi)的尸體忽然破棺而出遏餐,到底是詐尸還是另有隱情,我是刑警寧澤赢底,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布失都,位于F島的核電站柏蘑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏粹庞。R本人自食惡果不足惜咳焚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庞溜。 院中可真熱鬧革半,春花似錦、人聲如沸流码。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旅掂。三九已至,卻和暖如春访娶,著一層夾襖步出監(jiān)牢的瞬間商虐,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工崖疤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秘车,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓劫哼,卻偏偏與公主長(zhǎng)得像叮趴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子权烧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容