深入 JavaScript 的執行上下文

JavaScript中的執行上下文指的就是JavaScript的執行環境。根據 ECMA-262的第六版規範定義:

An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript implementation

JavaScript執行上下文可以理解爲編譯和執行JavaScript代碼時所處的抽象環境概念。JavaScript的代碼執行都是在執行上下文中進行的。ECMA-262 不同版本對JavaScript執行上下文的描述不同,在 ES5之前,執行環境有變量對象(VO)、活動對象(AO)的描述;而在ES5中引入了詞法環境和變量環境的概念,並定義了三種執行上下文,分別是:全局執行上下文、函數執行上下文、Eval 函數執行上下文。而ES6及之後的版本中,對詞法環境中的環境記錄進行了更明確的細分。本文將總結JavaScript的執行上下文環境及其創建過程。

 一、執行環境與作用域

最早接觸執行環境與作用域是在JavaScript高級程序設計中的概念。總結一下就是:

1、執行環境是一個隔離的環境,定義了環境中代碼有權訪問的數據。

程序設計中,代碼對變量的訪問權限控制很重要。通過最小特權原則,嚴格限制變量的訪問權限,js中是通過執行環境來實現的。

2、環境中的有一個關聯的變量對象,環境中定義的所有變量和函數都保存在這個對象中。

類似於在全局環境中使用 var聲明一個變量,該變量會自動變成全局對象window的屬性。在函數的局部環境中,聲明的變量和函數同一樣會掛載到一個對象上,成爲對象的屬性,該變量即變量對象。但與全局對象不同的是,變量對象無法通過代碼訪問。

3、代碼在環境中執行時,會創建變量對象的作用域鏈(scope chain)屬性,用於保證對變量和函數的有序訪問。

在當前執行環境中找不到對應的標識符,將會搜索變量對象的作用域鏈。作用域鏈不僅保存了當前環境的變量對象,還擁有外部環境的變量對象引用,因此可以“由內而外”的訪問訪問變量。

4、活動對象和變量對象實際上是一個對象,只是在JavaScript中編譯和執行階段不同的叫法。參考 

JavaScript中的編譯概念使用即時編譯或者預編譯表達更合適。

5、實際上,執行環境中還有 this 對象的概念上述未提及,但在第七章節做了補充。原文是this對象是在運行時基於函數的執行環境綁定的。

重要的概念是 this對象是運行時確定的,而非定義時。理解了這段就明白JavaScript中的this爲何如此變化莫測了。

二、 可執行代碼與執行上下文( ECMA-262 第五版)

ES5規範定義了執行上下文(執行環境)中的三種構成。分別是,詞法環境、可變環境 和this綁定。

1、詞法環境

ECMA-262 第五版中對詞法環境的定義:

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. A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment

翻譯過來就是,詞法環境是一種規範類型,用於根據 ECMAScript 代碼的詞法嵌套結構定義標識符與特定變量和函數的關聯。詞法環境由環境記錄和對外部詞法環境的可能爲 null 的引用組成。

通俗來說,詞法環境實際上就是對某個環境中的變量和函數聲明的記錄。類似於上述變量對象的概念。

環境記錄是變量和函數聲明存儲在詞法環境中的地方,有兩種類型的環境記錄,分別是,聲明式環境記錄對象式環境記錄。聲明式環境記錄顧名思義會儲存變量聲明和函數聲明,將標識符與對應的值相關聯。而對象式環境記錄會將標識符與某些對象的屬性相關聯。具體討論一下兩者之間的區別與聯繫。

聲明式環境記錄

通常情況下,聲明式環境記錄綁定的內容會被直接存儲在底層實現上,如虛擬機的寄存器上,以便於快速訪問,這也是與老版ES3中的激活對象主要區別。另外聲明式環境記錄項的特性允許使用完整的詞法尋址技術,無需任何作用域鏈查找即可直接訪問所需的變量。

聲明式環境記錄除了支持可變綁定之外,還提供不可變綁定。不可變綁定是一種標識符和值之間的關聯一旦建立就不能修改的綁定。如果綁定的內容是不可更改的,所有變量的地址在編譯時就可以確定,這樣js引擎在執行時就可以針對性進行優化。

使用聲明性環境記錄替換舊的激活對象最重要的原因就是執行效率。JavaScript語言的作者也提到:

—ES3 中的激活對象實現只是“一個bug”:“我注意到 ES5 中有一些真正的改進,特別是第 10 章現在使用聲明式綁定環境. ES1-3 濫用對象作爲作用域(在 1995 年的 JS 中,我又一次這樣做了,因爲它在快速實現語言所需的對象上節省大量的時間)是一個錯誤,而不是一個特性”

 使用僞代碼表示聲明式環境記錄:

environment = {
  // storage
  environmentRecord: {
    type: "declarative",
    // storage
  },
  // reference to the parent environment
  outer: <...>
};

 eval 函數會破壞V8的優化,以下代碼運行在Chrome 93 環境。

 

V8對於函數的執行進行了優化,沒有創建arguments對象,也沒有捕獲父函數中的變量。這樣的函數是輕量級的。加入了eval函數之後,環境記錄中增加了arguments對象和closure閉包,即父環境。eval函數的存在破壞了V8的內部的優化,因爲函數內部有內部函數,因此很難分析內部函數是否引用 arguments。

對象式環境記錄

對象環境記錄用於定義出現在全局上下文with語句內部的變量和函數關聯。每with執行語句時,都會創建一個帶有對象環境記錄的新詞法環境運行上下文的環境被這個新創建的環境替換with執行完畢上下文環境恢復到以前的狀態。

var a = 10;
var b = 20;
 
with ({a: 30}) {
  console.log(a + b); // 50
}
 
console.log(a + b); // 30, restored

使用僞代碼表示:

// initial state
context.lexicalEnvironment = {
  environmentRecord: {a: 10, b: 20},
  outer: null
};
 
// "with" executed
previousEnvironment = context.lexicalEnvironment;
 
withEnvironment = {
  environmentRecord: {a: 30},
  outer: context.lexicalEnvironment
};
 
// replace current environment
context.lexicalEnvironment = withEnvironment;
 
// "with" completed, restore the environment back
context.lexicalEnvironment = previousEnvironment;

由於對象環境記錄效率低下,with語句已經從ES5嚴格模式中刪除。

詞法環境中另一個重要的核心是外部環境引用。對外部環境的引用意味着它可以訪問其外部詞法環境。如果在當前詞法環境中找不到變量,JavaScript 引擎可以在外部環境中查找變量,類似於作用域鏈的概念。

2、可變環境

可變環境也是一個詞法環境,具有上面定義的詞法環境的所有屬性和組件。詞法環境與可變環境的區別在於,前者用於存儲函數聲明和變量(let const)綁定,而後者僅用於存儲變量(var)綁定。

3、this綁定

相關文章諸多,不再贅述了。this是在運行時綁定的,有默認綁定、隱式綁定、顯示綁定。

 

 

參考鏈接:

https://262.ecma-international.org/5.1/#sec-10.2

http://dmitrysoshnikov.com/

https://262.ecma-international.org/7.0/#

https://blog.csdn.net/szengtal/article/details/78726178

http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-2-lexical-environments-ecmascript-implementation/#structure-of-execution-context

https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0#

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章