我們通常將 JavaScript 歸類爲動態或解釋執行語言,但實際上它也是一門編譯語言,它有自己的編譯器形式,運行在 JavaScript 引擎
中。
每個 Web 瀏覽器都有自己的 JavaScript 引擎形式:Chrome 有 V8
,Mozilla 有 SpiderMonkey
等。這些 JavaScript 引擎的共同點都是將 JavaScript 代碼轉換爲編譯器可以理解的語言,然後執行它。
執行上下文 Execution Context
當 JavaScript 代碼運行的時候,運行 JavaScript 代碼的環境形成了執行上下文 ,執行上下文決定代碼可以訪問哪些變量、函數、對象等。
我們將執行上下文簡單視爲運行當前代碼的 environment / scope
,我們知道作用域分爲 global scope
和 local scope
。
類似的,執行上下文也分爲不同的類型:
全局執行上下文 - 代碼首次執行時候的默認環境,在代碼的整個執行過程中,只用一個全局執行上下文。
函數執行上下文 - 每當執行流程進入到一個函數體內部的時候,就會創建一個函數執行上下文,可以有任意數量的函數執行上下文。
執行棧/調用棧
JavaScript 是單線程的,瀏覽器只分配給 JavaScript 一個主線程,一次只能執行一個任務(函數),因此它在執行棧中對其他操作(事件和函數執行)形成一個任務隊列,排隊等候執行。
每當在瀏覽器中加載腳本時,棧 stack
中的第一個元素就是全局執行上下文
。當有函數執行時,將創建一個函數執行上下文
,並將其置於全局執行上下文
之上。一旦函數執行完成,它就會從執行堆棧中彈出,並將控制權交給它下面的上下文中。結合上面說到的,我們看一個例子:
var name = "global variable";
console.log(name)
function func1() {
console.log("func1 被調用了。")
func2();
}
function func2() {
console.log("func2 被調用了。");
}
func1();
當上述代碼在瀏覽器中加載時:
- Javascript 引擎創建一個全局執行上下文
global execution context
並將其推送到當前執行棧。 - 接着進行
func1()
被調用,然後 Javascript 引擎爲該函數創建一個新的函數執行上下文function execution context
並將其推送到全局執行上下文的頂部。 - 在執行
func1()
過程中,發現func2()
被調用,Javascript 引擎爲該函數創建一個新的執行上下文,並將其推送到func1()
執行上下文的頂部。 - 當
func2()
函數完成時,其執行上下文從當前堆棧彈出,將控制權交給其下面的執行上下文,即func1()
函數執行上下文。 -
func1()
完成後,其執行堆棧將從堆棧中刪除,將控制權交給全局執行上下文。一旦執行了所有代碼,JavaScript 引擎就會從當前堆棧中刪除全局執行上下文。
執行上下文階段
執行上下文主要有兩個階段:創建階段
和執行階段
,接下來我們將逐一進行介紹。
創建階段
在函數執行發生之前,JavaScript 引擎會做如下事情:
- 首先,爲每個函數或變量創建與外部環境的連接,這些函數形成作用域鏈
scope chain
。作用鏈告訴執行上下文它應該包含什麼,以及它應該在哪裏查找解析函數的引用和變量的值。(對於全局環境,外部環境爲 null。在全局作用域內的所有環境都將把全局環境作爲其外部環境)。 - 掃描作用鏈後,將創建一個
環境存儲器
,其中全局上下文
的創建和引用(Web瀏覽器中的窗口),變量、函數和函數參數的創建和引用在內存
中完成。 - 最後,在第一步中創建的每個執行上下文中確定
this
關鍵字的值(對於全局執行上下文,this 指向 window)。
我們可以將創建階段
使用僞代碼這樣表示:
creationPhase = { // 創建階段
'outerEnvironmentConnection': { // 創建外部連接
// 形成作用域鏈
},
'variableObjectMapping': {
// 變量、函數和函數參數的創建和引用在內存中完成。
},
'valueOfThis': {}, // 確定 this 的值
}
執行階段
這是代碼在創建階段
形成的執行上下文中的運行的階段,並且逐行分配變量值。
當執行開始時,JavaScript 引擎在其創建階段對象中查找執行函數的引用。如果在當前對象中沒有找到,它將沿着作用域鏈
繼續向上查找,直到它到達全局環境。
如果在全局環境中找不到函數引用,則將返回錯誤。如果找到了引用並且函數正確執行,那麼這個特定函數的執行上下文
將從棧中彈出,接着 JavaScript 引擎將移動到下一個函數,它們的函數執行上下文
將被加入到棧中並執行,以此類推。
讓我們通過示例來看看上面的兩個階段,以便更好地理解它。
let name = "webinfoq";
var title = "execution context";
const message = "hello world";
function func1(num) {
var author = "deepak";
let value = 3;
let func2 = function multiply() {
return num * value;
}
const fixed = "Divine";
function addFive() {
return num + 5;
}
}
func1(10);
因此,全局執行上下文
將如下表示:
globalExecutionObj = { // 全局執行s上下文
outerEnvironmentConnection: null, // 全局上下文外部環境爲 null
variableObjectMapping: {
name: uninitialized, // 在創建階段,let聲明的變量是未初始化狀態
title: undefined, // var 聲明的變量表示爲未定義
date: uninitialized, // 在創建階段,const聲明的變量是未初始化狀態
func1: <func1 reference>, func1 地址引用
},
this: window //Global Object
}
注意:let
和const
定義的變量在創建階段沒有任何與之關聯的值,但var
定義的變量在創建階段爲undefined
,
這就是爲什麼可以在va
r 聲明之前訪問變量,(得到的是undefined
), 在let
和const
聲明之前訪問會報錯(暫時性死區)。
這就是所謂的變量提升
,所有使用 var
聲明的變量都會被提升到作用域的頂部。
在執行階段
,完成對變量的賦值等操作。因此,在執行階段
,全局執行上下文global execution
看起來像這樣:
globalExectutionObj = { // 全局執行上下文
outerEnvironmentConnection: null,
variableObjectMapping: {
name: "webinfoq",
title: "execution context",
message: "hello world",
func1: pointer to function func1, // 指向func1的指針
},
this: window //Global Object
}
當執行到 func1()
時,將形成新的函數執行上下文 function execution global
,創建階段如下所示:
func1ExecutionObj = { // func1 函數執行上下文
outerEnvironmentConnection: Global, // 外部環境爲全局環境
variableObjectMapping: {
arguments: {
0: 10,
length: 1
},
num: 10,
author: undefined, // var 聲明的
value: uninitialized, // let 聲明的
func2: uninitialized, // let 聲明的
fixed: uninitialized, // const 聲明
addFive: pointer to function addFive() // 指向函數addFive的指針
},
this: Global Object or undefined
}
執行階段:
func1ExecutionObj = {
outerEnvironmentConnection: Global,
variableObjectMapping: {
arguments: { // 先處理 arguments 參數
0: 10,
length: 1
},
num: 10,
author: "deepak", //變量f賦值
val: 3,
func2: pointer to function func2()
fixed: "Divine"
addFive: pointer to function addFive()
},
this: Global Object or undefined
}
Javascript 引擎創建執行上下文
,調用棧
。當有函數執行時,引擎就會創建一個新的函數執行上下文
。最後所用函數執行完成後,將更新全局環境,然後全局代碼完成,程序結束。
瞭解更多請關注微信公衆號:webinfoq
。