大家好,我是歸思君 起因是最近瞭解JS執行上下文的時候,發現很多書籍和資料,包括《JavaScript高級程式設計》、《JavaScript權威指南》和網上的一些博客專欄,都是從 ES3 角度來談執行上下文,用ES6規範解讀的比較少,所以想從ES6的角度看一下執行上下文。 下麵我嘗試用ECMAScr ...
大家好,我是歸思君
起因是最近瞭解JS執行上下文的時候,發現很多書籍和資料,包括《JavaScript高級程式設計》、《JavaScript權威指南》和網上的一些博客專欄,都是從 ES3 角度來談執行上下文,用ES6規範解讀的比較少,所以想從ES6的角度看一下執行上下文。
下麵我嘗試用ECMAScript 6規範文檔,來聊聊執行上下文,文章主要從這幾個方面介紹:
- ES6規範中的詞法環境
- ES6規範中定義的執行上下文結構
- 從ES6規範看實際代碼的執行流程
一、 什麼是執行上下文
咱們先來看看 ES6 中怎麼定義執行上下文的:
An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript implementation. At any point in time, there is at most one execution context that is actually executing code. This is known as the running execution context.
A stack is used to track execution contexts. The running execution context is always the top element of this stack. A new execution context is created whenever control is transferred from the executable code associated with the currently running execution context to executable code that is not associated with that execution context. The newly created execution context is pushed onto the stack and becomes the running execution context.執行上下文是一種規範類型,用於跟蹤 ECMAScript 實現(也就是 JavaScript 語言)代碼的執行狀態。在任意(代碼執行)的時間點中,最多有一個執行上下文在實際執行代碼。這稱為運行執行上下文。
堆棧用於跟蹤執行上下文。正在運行的執行上下文始終是該堆棧的頂部元素。每當控制從與當前運行的執行上下文關聯的可執行代碼轉移到不與該執行上下文關聯的可執行代碼時,就會創建新的執行上下文。新創建的執行上下文被壓入堆棧併成為正在運行的執行上下文。
為什麼執行上下文是一種“specification device”呢?
因為 EcmaScript
實際上是由 ECMA(European Computer Manufactures Association, 歐洲電腦製造協會) 制定的一種語言規範,而像 JavaScript、Adobe ActionScript 都是 ECMAScript 的一種實現,所以上述描寫中的執行上下文,是一種在規範下的定義。
從上面的定義可知:
- 執行上下文是 JavaScript 執行代碼時的運行環境
- 跟蹤執行上下文的堆棧是執行上下文調用棧(call stack),正在運行的執行上下文的是棧頂元素
- 在執行上下文切換新的可執行代碼時,會創建新的執行上下文(函數調用)
在分析執行上下文時,先來瞭解一下詞法環境的概念:
二、Lexical Environments(詞法環境)
A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code.
詞法環境是一種規範類型,在詞法嵌套的 ECMAScript 代碼中,用於定義標識符與特定變數和函數關聯,也就是說JS中的變數和函數存在這個詞法環境中
通常當function
聲明,with
語句或try..catch
語句執行時,都會有一個新的詞法環境被創建
根據ES6的規範,Lexical Environments
主要由兩個部分組成:
A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment.
Environment Record
(環境記錄項)outer Lexical Environment
(外部詞法環境的引用)
1.Environment Record
(環境記錄項)
ES6規範中,是這樣定義Environment Record
的:
An Environment Record records the identifier bindings that are created within the scope of its associated Lexical Environment. It is referred to as the Lexical Environment’s EnvironmentRecord
一個環境記錄項記錄著,在其關聯的詞法環境內創建的標識符綁定,它被稱為詞法環境的環境記錄
可以將Environment Record
(環境記錄項)看成在存儲詞法環境中,與標識符綁定的變數和函數的對象。
從規範角度,Environment Record
(環境記錄項)可以視作一個面向對象結構的抽象類,並且擁有三個子類
For specification purposes Environment Record values are values of the Record specification type and can be thought of as existing in a simple object-oriented hierarchy where Environment Record is an abstract class with three concrete subclasses,
此外聲明式環境記錄項中還有function Environment Record
和 module Environment Record
兩種類型
declarative Environment Record
(聲明式環境記錄項)function Environment Record
(函數式環境記錄項)module Environment Record
(模塊式環境記錄項)
object Environment Record
(對象式環境記錄項)global Environment Record
(全局式環境記錄項)
1.1 declarative Environment Record
(聲明式環境記錄項)
Declarative Environment Records are used to define the effect of ECMAScript language syntactic elements such as FunctionDeclarations, VariableDeclarations, and Catch clauses that directly associate identifier bindings with ECMAScript language values.
聲明式環境記錄項用於定義那些將標識符與ECMAScript 語言值綁定的ECMAScript 語法元素,比如 FunctionDeclarations(function 聲明), VariableDeclarations(var 聲明), and Catch clauses(catch 語句)
像日常使用的var
,let
、const
、function
聲明的變數,就存放在declarative Environment Record
這種詞法環境中,比如如下變數和函數都會放在聲明式環境記錄項中:
//所有元素,包括a,b,c,e都綁定在聲明式環境記錄項中
function foo(a){
var b = 10;
function c() {}
}
try {
...
} catch(e){}
聲明式環境記錄項又分為function Environment Record
(函數式環境記錄項) 和module Environment Record
(模塊式環境記錄項)
function Environment Record
(函數式環境記錄項)
函數式環境記錄項是聲明式環境記錄項的一種,用於表示函數頂級作用域。有以下特殊情況需要註意:
- 除箭頭函數外的其他函數,其環境記錄項都會進行
this
綁定 - 非箭頭函數且有
super
引用的函數,其環境記錄項會包含從函數內部執行super
方法調用的狀態
除了聲明式環境記錄項的規範方法外,還有以下欄位:
欄位名 | 值 | 解釋 |
---|---|---|
[[thisValue]] |
任意值 | 用於此函數調用的this值 |
[[thisBindingStatus]] |
"lexical", "initialized", "uninitialized" | 如果值為"lexical",說明是箭頭函數,該函數也不會擁有this值 |
[[FunctionObject]] |
Object | 表示被調用的函數對象,一旦這個函數對象被調用,此環境記錄項就會創建 |
[[HomeObject]] |
Object, undefined |
如果該函數擁有super屬性值,並且不是箭頭函數。[[HomeObject]] 指函數作為方法綁定的對象,預設值為undefined |
[[NewTarget]] |
Object, undefined |
如果該環境記錄項是由[[Construct]] 內部方法創建的,[[NewTarget]] 的值是[[Construct]] 中newTarget參數的值。預設值為undefined |
module Environment Record
(模塊式環境記錄項)
A module Environment Record is a declarative Environment Record that is used to represent the outer scope of an ECMAScript Module. In additional to normal mutable and immutable bindings, module Environment Records also provide immutable import bindings which are bindings that provide indirect access to a target binding that exists in another Environment Record.
模塊式環境記錄項也是聲明式環境記錄項的一種,用於表示ECMAScript 模塊的外部範圍。除了正常的可變和不可變綁定之外,模塊環境記錄還提供不可變導入綁定,這些導入綁定提供了對另一個環境記錄中存在的目標綁定的間接訪問。
用自己的話解釋就是,它不僅包括模塊的頂級聲明外,還包括由模塊顯式導入的綁定。其outer
值指向全局環境的詞法環境:
moduleEnvironment = {
environmentRecord: {
...
}
//引用全局
outer: global.LexicalEnvironment
}
1.2 object Environment Record
(對象式環境記錄項)
Each object Environment Record is associated with an object called its binding object. An object Environment Record binds the set of string identifier names that directly correspond to the property names of its binding object.
每一個對象式環境記錄項都有一個關聯的對象,這個對象被稱作綁定對象。對象式環境記錄項直接將一系列標識符與其綁定對象的屬性名稱建立一一對應關係。
對象式環境記錄項記錄其綁定對象的屬性名稱以及對應值,比如對於一個對象和對應的對象式環境記錄項:
var obj = {
name: "obj",
number: 1
}
假設其在瀏覽器環境下,則其偽代碼如下:
obj.lexicalEnvironment = {
environmentRecord: { name: "obj", number: 1},
outer: window.lexicalEnvironment
}
此外,對象是環境記錄項用在with
聲明語句中,每當with語句執行時,都會創建一個帶有對象環境記錄的新詞法環境,比如下麵的代碼:
var a = 10;
var b = 20;
with ({a: 30}) {
//這裡創建了一個新詞法環境,
//內部的環境項和with內部聲明的對象一一綁定
console.log(a + b);//50
}
console.log(a + b);//30
假設其在瀏覽器全局作用域下,那麼其偽代碼如下:
//全局環境下詞法環境,初始狀態
window.lexicalEnvironment = {
environmentRecord: {a: 10, b: 20},
outer: null
};
//當執行到with語句時
//1.暫存當前詞法環境
previousEnvironment = window.lexicalEnvironment;
//2.創建一個新的詞法環境
withEnvironment = {
environmentRecord: {a:30},
outer: window.lexicalEnvironment
};
//3.替代當前詞法環境
window.lexicalEnvironment = withEnvironemt;
//with語句執行完後,複原詞法環境
context.lexicalEnvironment = previousEnvironment;
1.3 global Environment Record
(全局式環境記錄項)
A global Environment Record is used to represent the outer most scope that is shared by all of the ECMAScript Script elements that are processed in a common Realm (8.2). A global Environment Record provides the bindings for built-in globals (clause 18), properties of the global object, and for all top-level declarations (13.2.8, 13.2.10) that occur within a Script.
全局環境記錄用於表示在共同領域中處理的所有ECMAScript腳本元素共用的最外部作用域。全局環境記錄為內置全局變數,全局對象的屬性以及腳本中發生的所有頂級聲明提供了綁定。
用偽代碼可以表示為:
globalEnvironment = {
environmentRecord: {
type: "global",
},
outer: null
}
邏輯上全局式環境記錄項只有一個,當它實際上是對象式環境記錄項和聲明式環境記錄項的複合對象。全局式環境記錄項基於領域中的全局對象,此外包含所有內置全局變數的綁定,FunctionDeclaration
引入的所有綁定,以及GeneratorDeclaration
,AsyncFunctionDeclaration
,AsyncGeneratorDeclaration
或VariableStatement
全局代碼。
全局環境記錄項中有這些欄位
欄位名 | 值 | 解釋 |
---|---|---|
[[ObjectRecord]] |
Object Environment Record | 綁定對象是global object。 它包含全局內置綁定以及FunctionDeclaration , GeneratorDeclaration , 和 VariableDeclaration 在全局代碼中綁定相關聯的領域(realm). |
[[DeclarativeRecord]] |
Declarative Environment Record | 包含除了FunctionDeclaration ,GeneratorDeclaration 和VariableDeclaration 綁定之外的關聯作用域代碼的全局代碼中的所有聲明的綁定. |
[[VarNames]] |
List of String | 由相關領域的全局代碼中的FunctionDeclaration ,GeneratorDeclaration 和VariableDeclaration 聲明綁定的字元串名稱。 |
2.outer Lexical Environment
(外部詞法環境的引用)
The outer environment reference is used to model the logical nesting of Lexical Environment values. The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment.
外部詞法環境引用用於表示詞法環境的邏輯嵌套關係模型。(內部)詞法環境的外部引用是邏輯上包含內部詞法環境的詞法環境。
outer
是指向外部詞法環境的引用,它在不同環境下,其值會隨之不同:
- 全局環境下,沒有詞法環境對其進行包圍,所以其詞法環境的
outer
為null
- 模塊環境下,全局環境將其包圍,因此其
outer
指向全局環境的詞法環境 - 函數環境下,其外部詞法環境是該函數聲明時包圍其的詞法環境,比如:
//聲明a時,處於全局環境下,因此a詞法環境的outer指向全局環境的詞法環境
function a(){ //a:lexicalEnvironment.outer = global.lexicalEnvironment
console.log(name);
}
function b(){ //b:lexicalEnvironment.outer = global.lexicalEnvironment
var name = "b";
a();//global
}
var name = "global";
b();//global
如果將函數的聲明放在嵌套函數詞法環境內部:
function b(){ //b:lexicalEnvironment.outer = global.lexicalEnvironment
var name = "b";
//聲明a時,處於b的詞法環境下,因此a詞法環境的outer指向b的詞法環境
function a(){ //a:lexicalEnvironment.outer = b.lexicalEnvironment
console.log(name);
}
a();//b
}
var name = "global";
b();//b
發現沒,如果把嵌套的不同詞法環境的outer
值連接在一起,就形成了一條作用域鏈。
舉個例子,在瀏覽器環境下的多個嵌套函數,其作用域鏈為: foo3->foo2->foo1->windows
//作用域鏈 foo3->foo2->foo1->windows
function foo1() {
//...
function foo2() {
//...
function foo3() {
//...
}
}
}
介紹完詞法環境,下麵就進入正題,具體來看看執行上下文的結構:
三.執行上下文的結構
Execution context has state components
執行上下文擁有以下組件
組件 | Purpose |
---|---|
code evaluation state | Any state needed to perform, suspend, and resume evaluation of the code associated with this execution context. 記錄執行上下文代碼執行、掛起和恢復等狀態 |
Function | If this execution context is evaluating the code of a function object, then the value of this component is that function object. If the context is evaluating the code of a Script or Module, the value is null. 如果當前執行上下文正在執行的是函數對象的代碼,Function 值指向正在執行的函數對象,如果是執行的是腳本和模塊,該值為 null。正在運行的執行上下文的 Function 值也稱為活動函數對象 |
Realm | The Realm from which associated code accesses ECMAScript resources. 關聯代碼訪問ECMAScript資源,指代當前上下文所屬領域的資源,包括全局對象、與此領域相關的代碼使用的內在值等等,用於隔離其他領域 |
LexicalEnvironment |
Identifies the Lexical Environment used to resolve identifier references made by code within this execution context. 標識符,標識用於解析此執行上下文中引用的詞法環境,let 和const 聲明的變數會掛載到該標識符引用的詞法環境中 |
VariableEnvironment |
Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context. 標識符,標識詞法環境,其綁定由 var 聲明的EnvironmentRecord,也就是var聲明的變數會存儲在此環境中 |
Generator | The GeneratorObject that this execution context is evaluating. 記錄當前正在解析的執行器對象 |
用偽代碼表示:
ExecutionContext = {
codeEvaluationState,
Function,
Realm,
LexicalEnviroment: {...},
VariableEnvironment: {...},
Generator: {...},
}
1. code evaluation state
At some later time a suspended execution context may again become the running execution context and continue evaluating its code at the point where it had previously been suspended. Transition of the running execution context status among execution contexts usually occurs in stack-like last-in/first-out manner.
code evaluation state
是記錄當前上下文在上下文執行棧中的狀態,用於切換棧中的不同執行上下文,主要有:
- perform(執行)
- suspend(掛起)
- resume(恢復)
2. Function
Function
值是記錄當前執行上下文是否為函數執行上下文:
- 若當前是全局或函數執行上下文,其值為全局或函數
- 若當前是腳本_Script_或模塊_Module_ ,其值為
null
3. Realm
Before it is evaluated, all ECMAScript code must be associated with a Realm. Conceptually, a realm consists of a set of intrinsic objects, an ECMAScript global environment, all of the ECMAScript code that is loaded within the scope of that global environment, and other associated state and resources.
根據ES6的定義,所有ECMAScript
代碼都有一個與之關聯的Realm
領域。一個realm
由一系列內置對象,一個ECMAScript
全局環境,載入到全局環境中的ECMAScript
代碼以及其他關聯狀態和資源組成。
Realm
以Realm Record
的形式來表示,一個Realm Record
主要由以下欄位組成:
欄位名 | 值 | 解釋 |
---|---|---|
[[intrinsics]] |
Objects | 當前Realm中的內部固有對象,比如Object ,Function ,Boolean 等 |
[[globalThis]] |
Object | 當前Realm中的全局對象 |
[[globalEnv]] |
Lexical Environment | 當前Realm中的詞法環境 |
[[templateMap]] |
A List of Record | 當前Realm中的模版(比如字元串模版)的存儲信息,比如JavaScript具體實現中,是用來存儲模板字元串(template string)的緩存。下次再找模版會優先從此處查詢 |
Realm
是ECMAScipt
規範定義的一個概念,和上節提到的作用域概念有些重合。事實上Realm
包含了作用域概念,除了作用域的變數和函數,它還加上了內置對象,比如Object
,Function
,Boolean
等,以及載入到全局環境中的其他代碼等。
實際上在瀏覽器環境中,window
是就是一個Realm
, node中的global也是一個Realm
,對比我們平常熟知的作用域概念,Realm更符合JS代碼實際執行中需要的“執行環境”。
4. LexicalEnvironment
Identifies the Lexical Environment used to resolve identifier references made by code within this execution context.
標識用於解析在此執行上下文中由代碼創建的標識符引用的詞法環境。一般是
let
,const
聲明的變數存儲在該詞法環境中
這裡要和Lexical Environment
詞法環境(中間有空格)區分一下:
LexicalEnvironment
是執行上下文中的一個標識符,引用的是存儲let
,const
聲明變數的詞法環境Lexical Environment
是ES6規範定義的一個概念,包括 Environment Record 和 outer 引用兩個部分
5. VariableEnvironment
Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context.
標識執行上下文中的詞法環境,其詞法環境是在
var
聲明創建綁定的詞法環境,也就是這個詞法環境存儲的是var
聲明的變數
無論是LexicalEnvironment
還是LexicalEnvironment
,在執行上下文中都是詞法環境。在執行上下文創建時,其內部的LexicalEnvironment
和LexicalEnvironment
值相等。
除了這些欄位,執行上下文中還有一些抽象方法。下麵根據上下文中的抽象方法,來看看執行上下文中的this
值是怎樣變化的:
6. 執行上下文中的this
值
執行上下文中主要通過GetThisEnvironment ( )
來確定,來看看ES6規範裡面是怎麼說的:
The abstract operation GetThisEnvironment finds the Environment Record that currently supplies the binding of the keyword this.
抽象操作 GetThisEnvironment 查找當前提供關鍵字this綁定的環境記錄
執行上下文在實際執行中,通過調用GetThisEnvironment ( )
來獲取其this
綁定值。其具體執行步驟如下
GetThisEnvironment performs the following steps:
- Let lex be the running execution context’s LexicalEnvironment.
- Repeat
a. Let envRec be lex’s EnvironmentRecord.
b. Let exists be envRec.HasThisBinding().
c. If exists is true, return envRec.
d. Let outer be the value of lex’s outer environment reference.
e. Let lex be outer.
獲取當前執行上下文的this
值可以用如下偽代碼表示:
function GetThisEnvironment(){
var lex = currentLexicalEnvironment;
while(true){
var envRec = lex.EnvironmentRecord;
if(envRec.HasThisBinding()){
return envRec;
}
lex = envRec.outer;
}
}
//返回一個提供this綁定的環境記錄項
var envRec = GetThisEnvironment();
//通過環境記錄項內部抽象方法獲取this值
envRec.GetThisBinding();
四、 執行上下文棧 ( Call Execution stack )
先來看ES6規範中是如何定義執行上下文棧的:
At any point in time, there is at most one execution context that is actually executing code. This is known as the running execution context. A stack is used to track execution contexts. The running execution context is always the top element of this stack.
A new execution context is created whenever control is transferred from the executable code associated with the currently running execution context to executable code that is not associated with that execution context. The newly created execution context is pushed onto the stack and becomes the running execution context.
在任意(代碼執行)的時間點中,最多有一個執行上下文在實際執行代碼。這稱為運行執行上下文。堆棧用於跟蹤執行上下文。正在運行的執行上下文始終是該堆棧的頂部元素。
每當控制從與當前運行的執行上下文關聯的可執行代碼轉移到不與該執行上下文關聯的可執行代碼時,就會創建一個新的執行上下文。新創建的執行上下文被壓入堆棧併成為正在運行的執行上下文。
從ES6規範我們知道:
- 執行上下文棧是用來跟蹤執行上下文的,當前處於棧頂的是正在運行的執行上下文
- 調用其他關聯的可執行代碼時,會創建一個新的執行上下文,並將這個新的執行上下文壓入棧頂
藉助一個例子來說明:
function a() {
console.log("function a");
}
function b() {
console.log("function b");
a();
}
//執行b()
b();
在 chrome devtools 中debugger看執行上下文棧的執行情況:
第一步:在執行b()
前會創建一個全局執行上下文,就是下圖中的(anonymous)
第二步:將b()
函數執行上下文壓入棧中:
第三步:當b()
調用a()
時,將a()
函數執行上下文繼續壓入棧:
第四步:執行完a()
後,將a()
函數執行上下文出棧:
第五步:執行完b()
後,將b()
函數執行上下文出棧,最後只留下全局執行上下文
五、從 ECMAScript6 角度看代碼的執行流程
代碼的執行主要分為兩個階段:
- 編譯階段
- 執行階段
下麵以這一段代碼,用 ECMAScript 6 規範解讀代碼的執行流程。
var a = 10;
let b = 20;
const c = 30;
function add(d, e) {
var f = 40;
return d + e + f;
}
foo(50, 60);
在開始前,先回顧一下ES6規範中的執行上下文,用偽代碼表示:
ExecutionContext = {
codeEvaluationState, //記錄當前上下文在上下文執行棧中的狀態
Function, //當前執行上下文在執行中是否有函數對象,有的話Function值就指向這個函數對象
Realm, //當前執行上下文的領域/作用域
LexicalEnviroment: {...}, //let,const等變數聲明存儲在此類詞法環境
VariableEnvironment: {...},//var變數聲明存儲在此類詞法環境
Generator: {...},//當前執行上下文在執行中是否有生成器函數,有的話Generator值就指向這個生成器函數
}
在日常代碼分析中,在執行上下文中,對codeEvaluationState
,Function
,Realm
和Generator
關註的較少,我們著重分析LexicalEnviroment
,VariableEnvironment
和其記錄項中的this
綁定值。下麵就開始分析吧:
1.編譯階段
在這個階段,JS引擎會掃描變數和函數聲明,創建一個全局上下文,做好執行之前的代碼編譯和初始化工作,用偽代碼表示:
//全局上下文
GlobalExectionContext = {
//詞法環境
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// let和const變數聲明不會提升
b: < uninitialized >,
c: < uninitialized >,
// add 進行函數提升
add: < func >,
//記錄項的this值綁定到全局對象
ThisBinding: <Global Object>,
}
outerEnv: <null>,
},
//變數環境
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// var變數聲明會進行提升
a: undefined,
ThisBinding: <Global Object>
}
outerEnv: <null>,
}
}
上述的執行上下文對應代碼範圍如下:
var a = 10;
let b = 20;
const c = 30;
function add(d, e) {
var f = 40;
return d + e + f;
}
當add(d,e)
函數被調用時,會創建一個函數執行上下文,並將這個上下文壓入調用棧中,用偽代碼表示add(d, e)
函數執行上下文:
//add(d, e)函數執行上下文
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Arguments標識符綁定,並將實參傳入其中
Arguments: {0: 50, 1: 60, length: 2},
ThisBinding: <Global Object or undefined>,
},
outerEnv: <GlobalLexicalEnvironment>,
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
//var聲明變數提升
d: undefined,
ThisBinding: <Global Object or undefined>
},
outerEnv: <GlobalLexicalEnvironment>,
}
}
2.執行階段
執行階段主要是這一段代碼的執行:
foo(50, 60);
此時全局執行上下文的變化為:
let
和const
聲明的變數得到賦值:b 賦為 20,c賦為30var
聲明的變數 a 由 undefined覆蓋為 10
//全局上下文
GlobalExectionContext = {
//詞法環境
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// let和const聲明的變數得到賦值
b: 20,
c: 30,
add: < func >,
//記錄項的this值指向到全局對象
ThisBinding: <Global Object>,
}
outerEnv: <null>,
},
//變數環境
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// var變數聲明會進行提升
a: 10,
ThisBinding: <Global Object>
}
outerEnv: <null>,
}
}
函數執行上下文的變化為:
var
聲明的變數 f 由 undefined覆蓋為 40- 在
add(d, e)
函數執行上下文在執行完畢後,會返回計算結果值 150
//add(d, e)函數執行上下文
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Arguments標識符綁定,並將實參傳入其中
Arguments: {0: 50, 1: 60, length: 2},
ThisBinding: <Global Object or undefined>,
},
outerEnv: <GlobalLexicalEnvironment>,
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
d: 40,
},
ThisBinding: <Global Object or undefined>,
outerEnv: <GlobalLexicalEnvironment>,
}
}
在函數執行完畢後,該add(d,e)
函數執行上下文會出棧,該函數執行上下文內的變數也隨之銷毀。
參考文章
https://www.linkedin.com/pulse/javascript-under-hood-part-2-simple-example-execution-kabir
https://blog.openreplay.com/explaining-javascript-s-execution-context-and-stack/
https://blog.openreplay.com/explaining-javascript-s-execution-context-and-stack/