前言
本文是學(xué)習(xí)執(zhí)行上下文時(shí)做的學(xué)習(xí)筆記,非原創(chuàng),只是對(duì)知識(shí)的整理。
參考資料:
什么是執(zhí)行上下文?
執(zhí)行上下文是 評(píng)估和執(zhí)行 js 代碼的環(huán)境 的抽象概念。每當(dāng) js 代碼在運(yùn)行的時(shí)候,它都是在執(zhí)行上下文中運(yùn)行。
每個(gè)執(zhí)行上下文都有三個(gè)重要屬性:
- 變量對(duì)象 (Variable object, VO)
- 作用域鏈 (Scope chain)
- this
執(zhí)行上下文的三種類型
-
全局執(zhí)行上下文 —— 任何不在函數(shù)內(nèi)部的代碼都在全局上下文中。它會(huì)執(zhí)行兩件事:創(chuàng)建一個(gè)全局的 window 對(duì)象(瀏覽器的情況下),并且設(shè)置
this的值等于這個(gè)全局對(duì)象。一個(gè)程序中只會(huì)有一個(gè)全局執(zhí)行上下文。
-
函數(shù)執(zhí)行上下文 —— 每一個(gè)函數(shù)被調(diào)用時(shí),都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的上下文。每個(gè)函數(shù)都有自己的執(zhí)行上下文,不過是在函數(shù)被調(diào)用時(shí)創(chuàng)建的。函數(shù)上下文可以有任意多個(gè)。
在函數(shù)上下文中,我們用 活動(dòng)對(duì)象 (activation object, AO) 來表示變量對(duì)象。
活動(dòng)對(duì)象和變量對(duì)象其實(shí)是一個(gè)東西,只是變量對(duì)象是規(guī)范上的或者說是引擎實(shí)現(xiàn)上的,不可在 JavaScript 環(huán)境中訪問,只有到當(dāng)進(jìn)入一個(gè)執(zhí)行上下文中,這個(gè)執(zhí)行上下文的變量對(duì)象才會(huì)被激活,所以才叫 activation object ,而只有被激活的變量對(duì)象,也就是活動(dòng)對(duì)象上的各種屬性才能被訪問。
活動(dòng)對(duì)象是在進(jìn)入函數(shù)上下文時(shí)刻被創(chuàng)建的,它通過函數(shù)的 arguments 屬性初始化。arguments 屬性值是 Arguments 對(duì)象。
-
Eval 函數(shù)執(zhí)行上下文 —— eval() 函數(shù)可計(jì)算某個(gè)字符串,并執(zhí)行其中的 js 代碼。 執(zhí)行在
eval函數(shù)內(nèi)部的代碼葉惠有它自己的執(zhí)行上下文。不常用。
執(zhí)行棧
執(zhí)行棧,也在其他編程語言中稱為“調(diào)用?!保且环N擁有 LIFO (后進(jìn)先出)數(shù)據(jù)結(jié)構(gòu)的棧,被用來 存儲(chǔ)代碼運(yùn)行時(shí)創(chuàng)建的所有執(zhí)行上下文 。
當(dāng) js 引擎第一次遇到你的腳本時(shí),它會(huì)創(chuàng)建一個(gè)全局的執(zhí)行上下文并且壓入當(dāng)前執(zhí)行棧。每當(dāng)引擎遇到一個(gè)函數(shù)調(diào)用,它會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并壓入棧的頂部。
引擎會(huì)執(zhí)行那些執(zhí)行上下文位于棧頂?shù)暮瘮?shù)。當(dāng)該函數(shù)執(zhí)行結(jié)束時(shí),執(zhí)行上下文從棧中彈出,控制流程到達(dá)當(dāng)前棧中的下一個(gè)上下文。
執(zhí)行上下文的生命周期
創(chuàng)建
- 生成變量對(duì)象
- 創(chuàng)建作用域鏈
- 確定
this指向
執(zhí)行
- 變量賦值
執(zhí)行完畢后出棧,等待被回收
創(chuàng)建階段
在 js 代碼執(zhí)行前,執(zhí)行上下文將經(jīng)歷創(chuàng)建階段。在創(chuàng)建階段會(huì)發(fā)生三件事:
-
this綁定在全局執(zhí)行上下文中,
this的值指向全局對(duì)象。在瀏覽器中,this引用 Window 對(duì)象。在函數(shù)執(zhí)行上下文中,
this的值取決于該函數(shù)是如何被調(diào)用的。如果它被一個(gè)引用對(duì)象調(diào)用,那么this會(huì)被設(shè)置成那個(gè)對(duì)象,否則this的值就是全局對(duì)象或者undefined(嚴(yán)格模式下)。例如:let foo = { baz: function() { console.log(this); } } foo.baz(); // 'foo' let bar = foo.baz; bar(); // window 因?yàn)闆]有指定引用對(duì)象
-
創(chuàng)建 詞法環(huán)境 組件
ES6文檔定義:
詞法環(huán)境 是一種規(guī)范類型,基于 ECMAScript 代碼的詞法嵌套結(jié)構(gòu)來定義標(biāo)識(shí)符和具體變量和函數(shù)的關(guān)聯(lián)。一個(gè)詞法環(huán)境由環(huán)境記錄器和一個(gè)可能的引用外部詞法環(huán)境的空值組成。
簡(jiǎn)單來說 詞法環(huán)境 是一種持有標(biāo)識(shí)符—變量映射的結(jié)構(gòu)。(這里的標(biāo)識(shí)符指的是變量/函數(shù)的名字,而變量是對(duì)實(shí)際對(duì)象[包含函數(shù)類型對(duì)象]或原始數(shù)據(jù)的引用)。
現(xiàn)在,在詞法環(huán)境的內(nèi)部有兩個(gè)組件:(1) 環(huán)境記錄器和 (2) 一個(gè)外部環(huán)境的引用。
- 環(huán)境記錄器是存儲(chǔ)變量和函數(shù)聲明的實(shí)際位置。
- 外部環(huán)境的引用意味著它可以訪問其父級(jí)詞法環(huán)境(作用域)。
詞法環(huán)境有兩種類型:
-
全局環(huán)境(在全局執(zhí)行上下文中)是沒有外部環(huán)境引用的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用是 null。它擁有內(nèi)建的 Object/Array/等、在環(huán)境記錄器內(nèi)的原型函數(shù)(關(guān)聯(lián)全局對(duì)象,比如 window 對(duì)象)還有任何用戶定義的全局變量,并且
this的值指向全局對(duì)象。 - 在函數(shù)環(huán)境中,函數(shù)內(nèi)部用戶定義的變量存儲(chǔ)在環(huán)境記錄器中。并且引用的外部環(huán)境可能是全局環(huán)境,或者任何包含此內(nèi)部函數(shù)的外部函數(shù)。
環(huán)境記錄器也有兩種類型(如上?。?/p>
- 聲明式環(huán)境記錄器存儲(chǔ)變量、函數(shù)和參數(shù)。
- 對(duì)象環(huán)境記錄器用來定義出現(xiàn)在全局上下文中的變量和函數(shù)的關(guān)系。
簡(jiǎn)而言之,
- 在全局環(huán)境中,環(huán)境記錄器是對(duì)象環(huán)境記錄器。
- 在函數(shù)環(huán)境中,環(huán)境記錄器是聲明式環(huán)境記錄器。
注意 — 對(duì)于函數(shù)環(huán)境,聲明式環(huán)境記錄器還包含了一個(gè)傳遞給函數(shù)的 arguments 對(duì)象(此對(duì)象存儲(chǔ)索引和參數(shù)的映射)和傳遞給函數(shù)的參數(shù)的 length。
-
創(chuàng)建 變量環(huán)境 組件
它同樣是一個(gè)詞法環(huán)境,其環(huán)境記錄器持有變量聲明語句在執(zhí)行上下文中創(chuàng)建的綁定關(guān)系。
如上所述,變量環(huán)境也是一個(gè)詞法環(huán)境,所以它有著上面定義的詞法環(huán)境的所有屬性。
在 ES6 中,詞法環(huán)境組件和變量環(huán)境的一個(gè)不同就是前者被用來存儲(chǔ)函數(shù)聲明和變量(
let和const)綁定,而后者只用來存儲(chǔ)var變量綁定。
因此,變量對(duì)象會(huì)包括:
- 函數(shù)的所有形參 (如果是函數(shù)上下文)
- 由名稱和對(duì)應(yīng)值組成的一個(gè)變量對(duì)象的屬性被創(chuàng)建
- 沒有實(shí)參,屬性值設(shè)為 undefined
- 函數(shù)聲明
- 由名稱和對(duì)應(yīng)值(函數(shù)對(duì)象(function-object))組成一個(gè)變量對(duì)象的屬性被創(chuàng)建
- 如果變量對(duì)象已經(jīng)存在相同名稱的屬性,則完全替換這個(gè)屬性
- 變量聲明
- 由名稱和對(duì)應(yīng)值(undefined)組成一個(gè)變量對(duì)象的屬性被創(chuàng)建;
- 如果變量名稱跟已經(jīng)聲明的形式參數(shù)或函數(shù)相同,則變量聲明不會(huì)干擾已經(jīng)存在的這類屬性
執(zhí)行階段
在此階段,順序執(zhí)行代碼,完成對(duì)所有這些變量的分配,修改變量對(duì)象的值。
在執(zhí)行階段,如果 js 引擎不能在源碼中聲明的實(shí)際位置找到 let 變量的值,它會(huì)被賦值為 undefined