摘要:執行上下文當代碼運行的時候,運行代碼的環境形成了執行上下文,執行上下文決定代碼可以訪問哪些變量函數對象等。我們將執行上下文簡單視為運行當前代碼的,我們知道作用域分為和。完成后,其執行堆棧將從堆棧中刪除,將控制權交給全局執行上下文。
我們通常將 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 地址引用 }, this: window //Global Object }
注意:let 和 const 定義的變量在創建階段沒有任何與之關聯的值,但 var 定義的變量在創建階段為 undefined,
這就是為什么可以在 var 聲明之前訪問變量,(得到的是 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。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105624.html
摘要:除此以外,讓元素脫離文檔流也是一個很好的方法。因為元素一旦脫離文檔流,它對其他元素的影響幾乎為零,性能的損耗就能夠有效局限于一個較小的范圍。講完重排與重繪,往元素上綁定事件也是引起性能問題的元兇。高性能這本書非常精致,內容也非常豐富。 showImg(https://segmentfault.com/img/bVJgbt?w=600&h=784); 入手《高性能JavaScript》一...
摘要:調用棧就是為了到達當前執行位置所調用到的所用函數。方法測試是否至少有一個元素通過由提供的函數實現的測試返回值是終止。然而,如果存在于原型鏈上層,賦值語句的行為就會有些不同而且可能很出人意料。 typeof null 為 object 解釋 不同的對象在底層都表示為二進制,在JavaScript中二進制前三位都為0的話會被判斷為object類型,null 的二進制表示都是0,自然前三位都...
閱讀 891·2023-04-26 01:37
閱讀 3367·2021-09-02 15:40
閱讀 955·2021-09-01 10:29
閱讀 2887·2019-08-29 17:05
閱讀 3418·2019-08-28 18:02
閱讀 1180·2019-08-28 18:00
閱讀 1483·2019-08-26 11:00
閱讀 2602·2019-08-26 10:27