摘要:執(zhí)行上下文當(dāng)代碼運行的時候,運行代碼的環(huán)境形成了執(zhí)行上下文,執(zhí)行上下文決定代碼可以訪問哪些變量函數(shù)對象等。我們將執(zhí)行上下文簡單視為運行當(dāng)前代碼的,我們知道作用域分為和。完成后,其執(zhí)行堆棧將從堆棧中刪除,將控制權(quán)交給全局執(zhí)行上下文。
我們通常將 JavaScript 歸類為動態(tài)或解釋執(zhí)行語言,但實際上它也是一門編譯語言,它有自己的編譯器形式,運行在 JavaScript 引擎中。
每個 Web 瀏覽器都有自己的 JavaScript 引擎形式:Chrome 有 V8,Mozilla 有 SpiderMonkey 等。這些 JavaScript 引擎的共同點都是將 JavaScript 代碼轉(zhuǎn)換為編譯器可以理解的語言,然后執(zhí)行它。
執(zhí)行上下文 Execution Context當(dāng) JavaScript 代碼運行的時候,運行 JavaScript 代碼的環(huán)境形成了執(zhí)行上下文 ,執(zhí)行上下文決定代碼可以訪問哪些變量、函數(shù)、對象等。
我們將執(zhí)行上下文簡單視為運行當(dāng)前代碼的 environment / scope,我們知道作用域分為 global scope 和 local scope。
類似的,執(zhí)行上下文也分為不同的類型:
全局執(zhí)行上下文 - 代碼首次執(zhí)行時候的默認環(huán)境,在代碼的整個執(zhí)行過程中,只用一個全局執(zhí)行上下文。
函數(shù)執(zhí)行上下文 - 每當(dāng)執(zhí)行流程進入到一個函數(shù)體內(nèi)部的時候,就會創(chuàng)建一個函數(shù)執(zhí)行上下文,可以有任意數(shù)量的函數(shù)執(zhí)行上下文。
執(zhí)行棧/調(diào)用棧JavaScript 是單線程的,瀏覽器只分配給 JavaScript 一個主線程,一次只能執(zhí)行一個任務(wù)(函數(shù)),因此它在執(zhí)行棧中對其他操作(事件和函數(shù)執(zhí)行)形成一個任務(wù)隊列,排隊等候執(zhí)行。
每當(dāng)在瀏覽器中加載腳本時,棧 stack 中的第一個元素就是全局執(zhí)行上下文。當(dāng)有函數(shù)執(zhí)行時,將創(chuàng)建一個函數(shù)執(zhí)行上下文,并將其置于全局執(zhí)行上下文之上。一旦函數(shù)執(zhí)行完成,它就會從執(zhí)行堆棧中彈出,并將控制權(quán)交給它下面的上下文中。結(jié)合上面說到的,我們看一個例子:
var name = "global variable"; console.log(name) function func1() { console.log("func1 被調(diào)用了。") func2(); } function func2() { console.log("func2 被調(diào)用了。"); } func1();
當(dāng)上述代碼在瀏覽器中加載時:
Javascript 引擎創(chuàng)建一個全局執(zhí)行上下文 global execution context 并將其推送到當(dāng)前執(zhí)行棧。
接著進行 func1() 被調(diào)用,然后 Javascript 引擎為該函數(shù)創(chuàng)建一個新的函數(shù)執(zhí)行上下文 function execution context 并將其推送到全局執(zhí)行上下文的頂部。
在執(zhí)行 func1() 過程中,發(fā)現(xiàn) func2() 被調(diào)用,Javascript 引擎為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文,并將其推送到 func1() 執(zhí)行上下文的頂部。
當(dāng) func2() 函數(shù)完成時,其執(zhí)行上下文從當(dāng)前堆棧彈出,將控制權(quán)交給其下面的執(zhí)行上下文,即 func1() 函數(shù)執(zhí)行上下文。
func1() 完成后,其執(zhí)行堆棧將從堆棧中刪除,將控制權(quán)交給全局執(zhí)行上下文。一旦執(zhí)行了所有代碼,JavaScript 引擎就會從當(dāng)前堆棧中刪除全局執(zhí)行上下文。
執(zhí)行上下文階段執(zhí)行上下文主要有兩個階段:創(chuàng)建階段和執(zhí)行階段,接下來我們將逐一進行介紹。
創(chuàng)建階段在函數(shù)執(zhí)行發(fā)生之前,JavaScript 引擎會做如下事情:
首先,為每個函數(shù)或變量創(chuàng)建與外部環(huán)境的連接,這些函數(shù)形成作用域鏈 scope chain。作用鏈告訴執(zhí)行上下文它應(yīng)該包含什么,以及它應(yīng)該在哪里查找解析函數(shù)的引用和變量的值。(對于全局環(huán)境,外部環(huán)境為 null。在全局作用域內(nèi)的所有環(huán)境都將把全局環(huán)境作為其外部環(huán)境)。
掃描作用鏈后,將創(chuàng)建一個環(huán)境存儲器,其中全局上下文的創(chuàng)建和引用(Web瀏覽器中的窗口),變量、函數(shù)和函數(shù)參數(shù)的創(chuàng)建和引用在內(nèi)存中完成。
最后,在第一步中創(chuàng)建的每個執(zhí)行上下文中確定 this 關(guān)鍵字的值(對于全局執(zhí)行上下文,this 指向 window)。
我們可以將創(chuàng)建階段使用偽代碼這樣表示:
creationPhase = { // 創(chuàng)建階段 "outerEnvironmentConnection": { // 創(chuàng)建外部連接 // 形成作用域鏈 }, "variableObjectMapping": { // 變量、函數(shù)和函數(shù)參數(shù)的創(chuàng)建和引用在內(nèi)存中完成。 }, "valueOfThis": {}, // 確定 this 的值 }執(zhí)行階段
這是代碼在創(chuàng)建階段形成的執(zhí)行上下文中的運行的階段,并且逐行分配變量值。
當(dāng)執(zhí)行開始時,JavaScript 引擎在其創(chuàng)建階段對象中查找執(zhí)行函數(shù)的引用。如果在當(dāng)前對象中沒有找到,它將沿著作用域鏈繼續(xù)向上查找,直到它到達全局環(huán)境。
如果在全局環(huán)境中找不到函數(shù)引用,則將返回錯誤。如果找到了引用并且函數(shù)正確執(zhí)行,那么這個特定函數(shù)的執(zhí)行上下文將從棧中彈出,接著 JavaScript 引擎將移動到下一個函數(shù),它們的函數(shù)執(zhí)行上下文將被加入到棧中并執(zhí)行,以此類推。
讓我們通過示例來看看上面的兩個階段,以便更好地理解它。
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);
因此,全局執(zhí)行上下文將如下表示:
globalExecutionObj = { // 全局執(zhí)行s上下文 outerEnvironmentConnection: null, // 全局上下文外部環(huán)境為 null variableObjectMapping: { name: uninitialized, // 在創(chuàng)建階段,let聲明的變量是未初始化狀態(tài) title: undefined, // var 聲明的變量表示為未定義 date: uninitialized, // 在創(chuàng)建階段,const聲明的變量是未初始化狀態(tài) func1:, func1 地址引用 }, this: window //Global Object }
注意:let 和 const 定義的變量在創(chuàng)建階段沒有任何與之關(guān)聯(lián)的值,但 var 定義的變量在創(chuàng)建階段為 undefined,
這就是為什么可以在 var 聲明之前訪問變量,(得到的是 undefined), 在 let 和 const
聲明之前訪問會報錯(暫時性死區(qū))。
這就是所謂的變量提升,所有使用 var 聲明的變量都會被提升到作用域的頂部。
在執(zhí)行階段,完成對變量的賦值等操作。因此,在執(zhí)行階段,全局執(zhí)行上下文global execution 看起來像這樣:
globalExectutionObj = { // 全局執(zhí)行上下文 outerEnvironmentConnection: null, variableObjectMapping: { name: "webinfoq", title: "execution context", message: "hello world", func1: pointer to function func1, // 指向func1的指針 }, this: window //Global Object }
當(dāng)執(zhí)行到 func1() 時,將形成新的函數(shù)執(zhí)行上下文 function execution global,創(chuàng)建階段如下所示:
func1ExecutionObj = { // func1 函數(shù)執(zhí)行上下文 outerEnvironmentConnection: Global, // 外部環(huán)境為全局環(huán)境 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() // 指向函數(shù)addFive的指針 }, this: Global Object or undefined }
執(zhí)行階段:
func1ExecutionObj = { outerEnvironmentConnection: Global, variableObjectMapping: { arguments: { // 先處理 arguments 參數(shù) 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 引擎創(chuàng)建執(zhí)行上下文,調(diào)用棧。當(dāng)有函數(shù)執(zhí)行時,引擎就會創(chuàng)建一個新的函數(shù)執(zhí)行上下文。最后所用函數(shù)執(zhí)行完成后,將更新全局環(huán)境,然后全局代碼完成,程序結(jié)束。
了解更多請關(guān)注微信公眾號:webinfoq。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/105624.html
摘要:除此以外,讓元素脫離文檔流也是一個很好的方法。因為元素一旦脫離文檔流,它對其他元素的影響幾乎為零,性能的損耗就能夠有效局限于一個較小的范圍。講完重排與重繪,往元素上綁定事件也是引起性能問題的元兇。高性能這本書非常精致,內(nèi)容也非常豐富。 showImg(https://segmentfault.com/img/bVJgbt?w=600&h=784); 入手《高性能JavaScript》一...
摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個最重要的技術(shù)點常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實現(xiàn)的大排序算法一怪對象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術(shù)點 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...
摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個最重要的技術(shù)點常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實現(xiàn)的大排序算法一怪對象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個最重要的技術(shù)點 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...
摘要:調(diào)用棧就是為了到達當(dāng)前執(zhí)行位置所調(diào)用到的所用函數(shù)。方法測試是否至少有一個元素通過由提供的函數(shù)實現(xiàn)的測試返回值是終止。然而,如果存在于原型鏈上層,賦值語句的行為就會有些不同而且可能很出人意料。 typeof null 為 object 解釋 不同的對象在底層都表示為二進制,在JavaScript中二進制前三位都為0的話會被判斷為object類型,null 的二進制表示都是0,自然前三位都...
閱讀 900·2023-04-26 01:37
閱讀 3373·2021-09-02 15:40
閱讀 966·2021-09-01 10:29
閱讀 2898·2019-08-29 17:05
閱讀 3427·2019-08-28 18:02
閱讀 1184·2019-08-28 18:00
閱讀 1493·2019-08-26 11:00
閱讀 2615·2019-08-26 10:27