從 ECMAScript 6 角度談談執行上下文

来源:https://www.cnblogs.com/EthanWong/archive/2023/12/21/17918766.html
-Advertisement-
Play Games

大家好,我是歸思君 起因是最近瞭解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 Recordmodule Environment Record兩種類型

  • declarative Environment Record(聲明式環境記錄項)
    • function Environment Record(函數式環境記錄項)
    • module Environment Record(模塊式環境記錄項)
  • object Environment Record(對象式環境記錄項)
  • global Environment Record(全局式環境記錄項)

環境記錄項.png

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 語句)

像日常使用的varletconstfunction聲明的變數,就存放在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引入的所有綁定,以及GeneratorDeclarationAsyncFunctionDeclarationAsyncGeneratorDeclarationVariableStatement全局代碼。

全局環境記錄項中有這些欄位

欄位名 解釋
[[ObjectRecord]] Object Environment Record 綁定對象是global object。 它包含全局內置綁定以及FunctionDeclaration, GeneratorDeclaration, 和 VariableDeclaration在全局代碼中綁定相關聯的領域(realm).
[[DeclarativeRecord]] Declarative Environment Record 包含除了FunctionDeclarationGeneratorDeclarationVariableDeclaration綁定之外的關聯作用域代碼的全局代碼中的所有聲明的綁定.
[[VarNames]] List of String 由相關領域的全局代碼中的FunctionDeclarationGeneratorDeclarationVariableDeclaration聲明綁定的字元串名稱。

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是指向外部詞法環境的引用,它在不同環境下,其值會隨之不同:

  • 全局環境下,沒有詞法環境對其進行包圍,所以其詞法環境的outernull
  • 模塊環境下,全局環境將其包圍,因此其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. 標識符,標識用於解析此執行上下文中引用的詞法環境,letconst聲明的變數會掛載到該標識符引用的詞法環境中
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(恢復)

codeEvaluationStatus.png

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代碼以及其他關聯狀態和資源組成。

RealmRealm Record的形式來表示,一個Realm Record主要由以下欄位組成:

欄位名 解釋
[[intrinsics]] Objects 當前Realm中的內部固有對象,比如ObjectFunction,Boolean
[[globalThis]] Object 當前Realm中的全局對象
[[globalEnv]] Lexical Environment 當前Realm中的詞法環境
[[templateMap]] A List of Record 當前Realm中的模版(比如字元串模版)的存儲信息,比如JavaScript具體實現中,是用來存儲模板字元串(template string)的緩存。下次再找模版會優先從此處查詢

RealmECMAScipt規範定義的一個概念,和上節提到的作用域概念有些重合。事實上Realm包含了作用域概念,除了作用域的變數和函數,它還加上了內置對象,比如ObjectFunction,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 ,在執行上下文中都是詞法環境。在執行上下文創建時,其內部的LexicalEnvironmentLexicalEnvironment 值相等。

除了這些欄位,執行上下文中還有一些抽象方法。下麵根據上下文中的抽象方法,來看看執行上下文中的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:

  1. Let lex be the running execution context’s LexicalEnvironment.
  2. 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)
image-1.png

第二步:將b()函數執行上下文壓入棧中:
image-2.png

第三步:當b()調用a()時,將a()函數執行上下文繼續壓入棧:
image-3.png

第四步:執行完a()後,將a()函數執行上下文出棧:
image-4.png

第五步:執行完b()後,將b()函數執行上下文出棧,最後只留下全局執行上下文
image-5.png

image-6.png

五、從 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值就指向這個生成器函數
}

在日常代碼分析中,在執行上下文中,對codeEvaluationStateFunctionRealmGenerator 關註的較少,我們著重分析LexicalEnviromentVariableEnvironment和其記錄項中的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);

此時全局執行上下文的變化為:

  • letconst 聲明的變數得到賦值:b 賦為 20,c賦為30
  • var聲明的變數 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)函數執行上下文會出棧,該函數執行上下文內的變數也隨之銷毀。

參考文章

http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-2-lexical-environments-ecmascript-implementation/#declarative-environment-record

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/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ssh登錄 作用 在開發時,通常會用Terminal通過ssh登錄到每個伺服器里,打開伺服器終端,以進行開發,通常來說,一個伺服器會給出如下的幾個信息: host IP password 登錄伺服器 基本格式:ssh user@hostname user:用戶名 hostname:IP地址/功能變數名稱 如 ...
  • 問題 在調試接收串口數據的Qt程式中發現,數據存在延遲和粘包現象。下位機發送數據包頻率是100Hz,一包56位元組,波特率115200,在列印port->readAll()的值的時候發現並不是每10ms讀到一包數據,而是大概每50ms左右一次接收到5包數據,在其他電腦上調試,以及下載其他串口助手調試後 ...
  • 一、Mycat-web介紹 Mycat-web(現改名為Mycat-eye)是對Mycat-server提供監控服務,通過JDBC連接對Mycat,MySQL監控,監控遠程伺服器的cpu,記憶體,網路,磁碟等情況的使用。Mycat-web運行過程中需要依賴zookeeper,因此需要先安裝zookee ...
  • Flink是一個分散式系統,需要有效地分配和管理計算資源才能執行流應用程式。它集成了所有常見的集群資源管理器,如Hadoop YARN和Kubernetes,但也可以設置為作為一個獨立的集群運行,甚至作為一個庫。 Flink集群的剖析 Flink運行時由兩種類型的進程組成:一個JobManager和 ...
  • 最近開始體驗FastGPT開源知識庫問答系統,用他們試著開發調試一些小助手。這中間需要使用到Redis,就在自己伺服器上進行了安裝,特此記錄下。 環境說明:阿裡雲ECS,2核8G,X86架構,CentOS 7.9操作系統。 選擇版本 1.打開Redis官網下載頁面,可以選擇需要的版本下載。我這裡選擇 ...
  • 文章摘要 安卓架構技術主要包括MVC、MVP、MVVM等。下麵分別對這些架構技術進行分析優劣勢,並附上代碼示例。 正文 MVC(Model-View-Controller)架構 MVC是一種常用的軟體架構,它將應用程式分為三個主要組成部分:Model(模型)、View(視圖)和Controller( ...
  • 成功路上並不擁擠,因為堅持的人並不多。只有堅持了,我們才知道,這一路上有多少事情需要實幹,有多少東西需要學習。 1. 環境變數的理解 環境變數一般是指在操作系統中用來指定操作系統運行環境的一些參數,如臨時文件夾位置、系統文件夾位置以及某些應用軟體文件的路徑等等。環境變數相當於給系統或用戶應用程式設置 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 unplugin-generate-component-name 一款用於以文件夾名或者setup標簽寫入名字來自動生成Vue組件名的插件。 項目地址 功能 💚 支持 Vue 3 開箱即用。 ⚡️ 支持 Vite、Webpack、Rsp ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...