作用域和作用域鏈

一险耀、執(zhí)行環(huán)境execution context

定義:

執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù)觉痛,決定了他們各自的行為荠瘪。每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象(VO),環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中铐达。

獨(dú)家解毒:每個函數(shù)都有自己的執(zhí)行環(huán)境岖赋,當(dāng)執(zhí)行進(jìn)入一個函數(shù)時(shí)檬果,函數(shù)的執(zhí)行環(huán)境就會被推入一個執(zhí)行環(huán)境棧的頂部并獲取執(zhí)行權(quán)瓮孙。當(dāng)這個函數(shù)執(zhí)行完畢,它的執(zhí)行環(huán)境又從這個棧的頂部被刪除选脊,并把執(zhí)行權(quán)并還給之前執(zhí)行環(huán)境杭抠。這就是ECMAScript程序中的執(zhí)行流

也可以這樣解讀:當(dāng)調(diào)用一個 JavaScript 函數(shù)時(shí)恳啥,該函數(shù)就會進(jìn)入與該函數(shù)相對應(yīng)的執(zhí)行環(huán)境偏灿。如果又調(diào)用了另外一個函數(shù),則又會創(chuàng)建一個新的執(zhí)行環(huán)境钝的,并且在函數(shù)調(diào)用期間執(zhí)行過程都處于該環(huán)境中翁垂。當(dāng)調(diào)用的函數(shù)返回后铆遭,執(zhí)行過程會返回原始執(zhí)行環(huán)境。因而沿猜,運(yùn)行中的 JavaScript 代碼就構(gòu)成了一個執(zhí)行環(huán)境棧枚荣。

當(dāng)函數(shù)被調(diào)用時(shí)函數(shù)的局部環(huán)境被創(chuàng)建(函數(shù)內(nèi)的代碼執(zhí)行完畢后,該環(huán)境被銷毀啼肩,同時(shí)保存在其中的所有變量和函數(shù)定義也隨之被銷毀)橄妆。

全局執(zhí)行環(huán)境

全局執(zhí)行環(huán)境是最外圍的一個執(zhí)行環(huán)境。在Web瀏覽器中祈坠,全局執(zhí)行環(huán)境被認(rèn)為是window對象害碾,因此所有全局變量和函數(shù)都是作為window對象的屬性和方法創(chuàng)建的。某個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后赦拘,該環(huán)境被銷毀慌随,保存在其中的所有變量和函數(shù)定義也隨之銷毀(全局執(zhí)行環(huán)境知道應(yīng)用程序退出–例如關(guān)閉網(wǎng)頁或?yàn)g覽器—時(shí)才會被銷毀)

函數(shù)內(nèi)執(zhí)行環(huán)境

當(dāng)執(zhí)行流進(jìn)入一個函數(shù)時(shí),函數(shù)的環(huán)境就會被推入一個環(huán)境棧中躺同。而在函數(shù)執(zhí)行后儒陨,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境笋籽。

執(zhí)行環(huán)境的建立

創(chuàng)建階段

發(fā)生在函數(shù)調(diào)用時(shí)蹦漠,但在執(zhí)行具體代碼之前。具體完成創(chuàng)建作用域鏈车海、創(chuàng)建變量笛园、函數(shù)和參數(shù)以及求this(講this的時(shí)候講)的值 。

在創(chuàng)建執(zhí)行環(huán)境階段會進(jìn)行函數(shù)的定義侍芝,此時(shí)處于函數(shù)定義期

? 函數(shù)定義的時(shí)候研铆,都會創(chuàng)建一個[[scope]]屬性,通這個對象對應(yīng)的是一個對象的列表州叠,列表中的對象僅能javascript內(nèi)部訪問棵红,沒法通過語法訪問。

我們定義一全局函數(shù)A咧栗,那么A函數(shù)就創(chuàng)建了一個A的[[scope]]屬性逆甜。此時(shí),[[scope]]里面只包含了全局對象【Global Object】致板。

而如果交煞, 我們在A的內(nèi)部定義一個B函數(shù),那B函數(shù)同樣會創(chuàng)建一個[[scope]]屬性斟或,B的[[scope]]屬性包含了兩個對象素征,一個是A的活動對象Activation Object、一個是全局對象,A的活動對象在前面御毅,全局對象排在后面根欧。

簡而言之,一個函數(shù)的[Scope]屬性中對象列表的順序是上一層函數(shù)的Activation Object對象端蛆,然后是上上層的咽块,一直到最外層的全局對象。

注意這重要的一點(diǎn)--[[scope]]在函數(shù)創(chuàng)建時(shí)被存儲--靜態(tài)(不變的)欺税,永遠(yuǎn)永遠(yuǎn)侈沪,直至函數(shù)銷毀。即:函數(shù)可以永不調(diào)用晚凿,但[[scope]]屬性已經(jīng)寫入亭罪,并存儲在函數(shù)對象中。

* 變量對象VO

每一個執(zhí)行環(huán)境都對應(yīng)一個變量對象歼秽,在該執(zhí)行環(huán)境中定義的所有變量和函數(shù)都存放在其對應(yīng)的變量對象中应役。

(1)進(jìn)入執(zhí)行上下文時(shí),VO的初始化過程如下:

函數(shù)的形參:變量對象的一個屬性燥筷,其屬性名就是形參的名字箩祥,其值就是實(shí)參的值;對于沒有傳遞的參數(shù)肆氓,其值為undefined袍祖;

函數(shù)聲明:變量對象的一個屬性,其屬性名和屬性值都是函數(shù)對象創(chuàng)建出來的谢揪,如果變量對象已經(jīng)辦好了相同名字的屬性蕉陋,則替換它的值

變量聲明:變量對象的一個屬性,其屬性名即為變量名拨扶,其值為undefined凳鬓;如果變量名和已經(jīng)聲明的函數(shù)名或者函數(shù)的參數(shù)名,則不會影響已經(jīng)存在的屬性

(2)執(zhí)行代碼階段患民,變量對象中的一些屬性undefined值將會確定(VO變成AO)

這里需要說明一下:函數(shù)表達(dá)式不包含在變量對象之中

function foo(x, y) {
  var z = 30;
  function bar() {} // 函數(shù)聲明
  (function baz() {}); // 函數(shù)表達(dá)式
}

foo(10, 20);
scope2.jpg
* 活動對象AO

未進(jìn)入執(zhí)行階段之前缩举,變量對象中的屬性都不能訪問!(具體實(shí)現(xiàn)的引擎可以訪問匹颤,執(zhí)行代碼是不能訪問的)仅孩。但是進(jìn)入執(zhí)行階段之后,變量對象轉(zhuǎn)變?yōu)榱?strong>活動對象惋嚎,里面的屬性都能被訪問了杠氢,然后開始進(jìn)行執(zhí)行階段的操作站刑。所以活動對象實(shí)際就是變量對象在真正執(zhí)行時(shí)的另一種形式另伍。

其實(shí)就是加了一個鎖,函數(shù)中的程序未執(zhí)行的時(shí)候(進(jìn)入上下文階段)有鎖,不能訪問摆尝,而進(jìn)入執(zhí)行階段則可以訪問温艇,變量對象變?yōu)榛顒訉ο蟆?/p>

*作用域?qū)ο骩[scope]]

函數(shù)定義時(shí)創(chuàng)建的一個包含函數(shù)變量方法的可訪問權(quán)限的特殊對象

* 作用域鏈scope chains

作用域鏈的原理和原型鏈很類似,如果這個變量在自己的作用域中沒有堕汞,那么它會尋找父級的勺爱,直到最頂層

可訪問變量或方法scope chains= 該執(zhí)行環(huán)境的AO + [[Scope]]

var x = 10;
 
(function foo() {
  var y = 20;
  (function bar() {
    var z = 30;
    // "x"和"y"是自由變量
    // 會在作用域鏈的下一個對象中找到(函數(shù)”bar”的互動對象之后)
    console.log(x + y + z);
  })();
})();
scope1.jpg

執(zhí)行階段

主要完成變量賦值、函數(shù)引用和解釋/執(zhí)行其他代碼

總的來說可以將執(zhí)行上下文看作是一個對象
     EC(execution context) = {
           VO:{/*函數(shù)中的arguments對象讯检、參數(shù)琐鲁、內(nèi)部變量以及函數(shù)聲明*/}
           this:{},
           Scope:{/*AO以及所有父執(zhí)行上下文中的AO*/}
    
}
scope0.jpg

完整Demo:


// 第一步頁面載入創(chuàng)全局執(zhí)行環(huán)境global executing context和全局活動象
// 定義全局[[scope]],只含有Window對象
// 掃描全局的定義變量及函數(shù)對象:color【undefined】、changecolor【FD創(chuàng)建changecolor的[[scope]]人灼,
// 此時(shí)里面 只含有全局活動對象】,加入到window中围段,所以全局變量和全局函數(shù)對象都是做為window的屬性定義的。
// 程序已經(jīng)定義好所以在此執(zhí)行環(huán)境內(nèi)任何位置都可以執(zhí)行changecolor(),color也已經(jīng)被定義投放,但是它的值是undefined
 
// 第二步color賦值"blue"
var color = "blue";
 
// 它是不需要賦值的奈泪,它就是引用本身
function changecolor() {
 // 第四步進(jìn)入changecolor的執(zhí)行環(huán)境
 // 創(chuàng)建活動對象,掃描定義變量和定義函數(shù)灸芳,anothercolor【undefined】和swapcolors【FD創(chuàng)建swapcolors的[[scope]]加入changecolor的活動對象和全局活動對象】加入到活動對象,活動對象中同時(shí)還要加入arguments和this
 // 活動對象推入scope chain 頂端
 // 程序已經(jīng)定義好所以在此執(zhí)行環(huán)境內(nèi)任何位置都可以執(zhí)行swapcolors(),anothercolor也已經(jīng)被定義好涝桅,但它的值是undefined
  
 // 第五anothercolor賦值"red"
 var anothercolor = "red";
  
 // 它是不需要賦值的,它就是引用本身
 function swapcolors() {
  // 第七步進(jìn)入swapcolors的執(zhí)行環(huán)境烙样,創(chuàng)建它的活動對象
  // 復(fù)制swapcolors的[[scope]]到scope chain
  // 掃描定義變量和定義函數(shù)對象冯遂,活動對象中加入變量tempcolor【undefined】以及arguments和this
  // 活動對象推入scope chain 頂端
   
  // 第八步tempcolor賦值anothercolor,anothercolor和color會沿著scope chain被查到,并繼續(xù)往下執(zhí)行
  var tempcolor = anothercolor;
   anothercolor = color;
   color = tempcolor;
 }
 
 // 第六步執(zhí)行swapcolors,進(jìn)入其執(zhí)行環(huán)境
 swapcolors();
}
 
// 第三步執(zhí)行changecolor,進(jìn)入其執(zhí)行環(huán)境
changecolor();
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谒获,一起剝皮案震驚了整個濱河市债蜜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌究反,老刑警劉巖寻定,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異精耐,居然都是意外死亡狼速,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門卦停,熙熙樓的掌柜王于貴愁眉苦臉地迎上來向胡,“玉大人,你說我怎么就攤上這事惊完〗┣郏” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵小槐,是天一觀的道長拇派。 經(jīng)常有香客問我荷辕,道長,這世上最難降的妖魔是什么件豌? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任疮方,我火速辦了婚禮,結(jié)果婚禮上茧彤,老公的妹妹穿的比我還像新娘骡显。我一直安慰自己,他們只是感情好曾掂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布惫谤。 她就那樣靜靜地躺著,像睡著了一般珠洗。 火紅的嫁衣襯著肌膚如雪石挂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天险污,我揣著相機(jī)與錄音痹愚,去河邊找鬼。 笑死蛔糯,一個胖子當(dāng)著我的面吹牛拯腮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚁飒,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼动壤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淮逻?” 一聲冷哼從身側(cè)響起琼懊,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎爬早,沒想到半個月后哼丈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筛严,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年醉旦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桨啃。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡车胡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出照瘾,到底是詐尸還是另有隱情匈棘,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布析命,位于F島的核電站主卫,受9級特大地震影響逃默,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜队秩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一笑旺、第九天 我趴在偏房一處隱蔽的房頂上張望昼浦。 院中可真熱鬧馍资,春花似錦、人聲如沸关噪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽使兔。三九已至建钥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虐沥,已是汗流浹背熊经。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留欲险,地道東北人镐依。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像天试,于是被迫代替她去往敵國和親槐壳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350