JavaScript中的執(zhí)行上下文和執(zhí)行棧的概念-創(chuàng)新互聯(lián)

本篇內(nèi)容介紹了“JavaScript中的執(zhí)行上下文和執(zhí)行棧的概念”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供祥符網(wǎng)站建設(shè)、祥符做網(wǎng)站、祥符網(wǎng)站設(shè)計(jì)、祥符網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、祥符企業(yè)網(wǎng)站模板建站服務(wù),十余年祥符做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

什么是執(zhí)行上下文?

簡而言之,執(zhí)行上下文是評估和執(zhí)行 JavaScript 代碼的環(huán)境的抽象概念。每當(dāng) Javascript 代碼在運(yùn)行的時(shí)候,它都是在執(zhí)行上下文中運(yùn)行。

執(zhí)行上下文的類型

JavaScript 中有三種執(zhí)行上下文類型。

  • 全局執(zhí)行上下文 — 這是默認(rèn)或者說基礎(chǔ)的上下文,任何不在函數(shù)內(nèi)部的代碼都在全局上下文中。它會執(zhí)行兩件事:創(chuàng)建一個(gè)全局的 window 對象(瀏覽器的情況下),并且設(shè)置 this 的值等于這個(gè)全局對象。一個(gè)程序中只會有一個(gè)全局執(zhí)行上下文。

  • 函數(shù)執(zhí)行上下文 — 每當(dāng)一個(gè)函數(shù)被調(diào)用時(shí), 都會為該函數(shù)創(chuàng)建一個(gè)新的上下文。每個(gè)函數(shù)都有它自己的執(zhí)行上下文,不過是在函數(shù)被調(diào)用時(shí)創(chuàng)建的。函數(shù)上下文可以有任意多個(gè)。每當(dāng)一個(gè)新的執(zhí)行上下文被創(chuàng)建,它會按定義的順序(將在后文討論)執(zhí)行一系列步驟。

  • Eval 函數(shù)執(zhí)行上下文 — 執(zhí)行在 eval 函數(shù)內(nèi)部的代碼也會有它屬于自己的執(zhí)行上下文,但由于 JavaScript 開發(fā)者并不經(jīng)常使用 eval,所以在這里我不會討論它。

執(zhí)行棧

執(zhí)行棧,也就是在其它編程語言中所說的“調(diào)用棧”,是一種擁有 LIFO(后進(jìn)先出)數(shù)據(jù)結(jié)構(gòu)的棧,被用來存儲代碼運(yùn)行時(shí)創(chuàng)建的所有執(zhí)行上下文。

當(dāng) JavaScript 引擎第一次遇到你的腳本時(shí),它會創(chuàng)建一個(gè)全局的執(zhí)行上下文并且壓入當(dāng)前執(zhí)行棧。每當(dāng)引擎遇到一個(gè)函數(shù)調(diào)用,它會為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并壓入棧的頂部。

引擎會執(zhí)行那些執(zhí)行上下文位于棧頂?shù)暮瘮?shù)。當(dāng)該函數(shù)執(zhí)行結(jié)束時(shí),執(zhí)行上下文從棧中彈出,控制流程到達(dá)當(dāng)前棧中的下一個(gè)上下文。

讓我們通過下面的代碼示例來理解:

let a = 'Hello World!';
function first() {
 console.log('Inside first function');
 second();
 console.log('Again inside first function');
}
function second() {
 console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');

上述代碼的執(zhí)行上下文棧。

JavaScript中的執(zhí)行上下文和執(zhí)行棧的概念

當(dāng)上述代碼在瀏覽器加載時(shí),JavaScript 引擎創(chuàng)建了一個(gè)全局執(zhí)行上下文并把它壓入當(dāng)前執(zhí)行棧。當(dāng)遇到 first() 函數(shù)調(diào)用時(shí),JavaScript 引擎為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并把它壓入當(dāng)前執(zhí)行棧的頂部。

當(dāng)從 first() 函數(shù)內(nèi)部調(diào)用 second() 函數(shù)時(shí),JavaScript 引擎為 second() 函數(shù)創(chuàng)建了一個(gè)新的執(zhí)行上下文并把它壓入當(dāng)前執(zhí)行棧的頂部。當(dāng) second() 函數(shù)執(zhí)行完畢,它的執(zhí)行上下文會從當(dāng)前棧彈出,并且控制流程到達(dá)下一個(gè)執(zhí)行上下文,即 first() 函數(shù)的執(zhí)行上下文。

當(dāng) first() 執(zhí)行完畢,它的執(zhí)行上下文從棧彈出,控制流程到達(dá)全局執(zhí)行上下文。一旦所有代碼執(zhí)行完畢,JavaScript 引擎從當(dāng)前棧中移除全局執(zhí)行上下文。

怎么創(chuàng)建執(zhí)行上下文?

到現(xiàn)在,我們已經(jīng)看過 JavaScript 怎樣管理執(zhí)行上下文了,現(xiàn)在讓我們了解 JavaScript 引擎是怎樣創(chuàng)建執(zhí)行上下文的。
創(chuàng)建執(zhí)行上下文有兩個(gè)階段:1) 創(chuàng)建階段 和 2) 執(zhí)行階段。

The Creation Phase

在 JavaScript 代碼執(zhí)行前,執(zhí)行上下文將經(jīng)歷創(chuàng)建階段。在創(chuàng)建階段會發(fā)生三件事:

  • this 值的決定,即我們所熟知的 This 綁定。

  • 創(chuàng)建詞法環(huán)境組件。

  • 創(chuàng)建變量環(huán)境組件。

所以執(zhí)行上下文在概念上表示如下:

ExecutionContext = {
 ThisBinding = <this value>,
 LexicalEnvironment = { ... },
 VariableEnvironment = { ... },
}

This 綁定:

在全局執(zhí)行上下文中,this 的值指向全局對象。(在瀏覽器中,this引用 Window 對象)。

在函數(shù)執(zhí)行上下文中,this 的值取決于該函數(shù)是如何被調(diào)用的。如果它被一個(gè)引用對象調(diào)用,那么 this 會被設(shè)置成那個(gè)對象,否則 this 的值被設(shè)置為全局對象或者 undefined(在嚴(yán)格模式下)。例如:

let foo = {
 baz: function() {
 console.log(this);
 }
}

foo.baz();  // 'this' 引用 'foo', 因?yàn)?nbsp;'baz' 被
       // 對象 'foo' 調(diào)用

let bar = foo.baz;

bar();    // 'this' 指向全局 window 對象,因?yàn)?       // 沒有指定引用對象

詞法環(huán)境

官方的 ES6 文檔把詞法環(huán)境定義為

詞法環(huán)境是一種規(guī)范類型,基于 ECMAScript 代碼的詞法嵌套結(jié)構(gòu)來定義標(biāo)識符和具體變量和函數(shù)的關(guān)聯(lián)。一個(gè)詞法環(huán)境由環(huán)境記錄器和一個(gè)可能的引用外部詞法環(huán)境的空值組成。

簡單來說詞法環(huán)境是一種持有標(biāo)識符—變量映射的結(jié)構(gòu)。(這里的標(biāo)識符指的是變量/函數(shù)的名字,而變量是對實(shí)際對象[包含函數(shù)類型對象]或原始數(shù)據(jù)的引用)。
現(xiàn)在,在詞法環(huán)境的內(nèi)部有兩個(gè)組件:(1) 環(huán)境記錄器和 (2) 一個(gè)外部環(huán)境的引用。

  1. 環(huán)境記錄器是存儲變量和函數(shù)聲明的實(shí)際位置。

  2. 外部環(huán)境的引用意味著它可以訪問其父級詞法環(huán)境(作用域)。

詞法環(huán)境有兩種類型:

  • 全局環(huán)境(在全局執(zhí)行上下文中)是沒有外部環(huán)境引用的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用是 null。它擁有內(nèi)建的 Object/Array/等、在環(huán)境記錄器內(nèi)的原型函數(shù)(關(guān)聯(lián)全局對象,比如 window 對象)還有任何用戶定義的全局變量,并且 this的值指向全局對象。

  • 在函數(shù)環(huán)境中,函數(shù)內(nèi)部用戶定義的變量存儲在環(huán)境記錄器中。并且引用的外部環(huán)境可能是全局環(huán)境,或者任何包含此內(nèi)部函數(shù)的外部函數(shù)。

環(huán)境記錄器也有兩種類型(如上?。?/p>

  • 聲明式環(huán)境記錄器存儲變量、函數(shù)和參數(shù)。

  • 對象環(huán)境記錄器用來定義出現(xiàn)在全局上下文中的變量和函數(shù)的關(guān)系。

簡而言之,

在全局環(huán)境中,環(huán)境記錄器是對象環(huán)境記錄器。
在函數(shù)環(huán)境中,環(huán)境記錄器是聲明式環(huán)境記錄器。

注意 — 對于函數(shù)環(huán)境,聲明式環(huán)境記錄器還包含了一個(gè)傳遞給函數(shù)的 arguments 對象(此對象存儲索引和參數(shù)的映射)和傳遞給函數(shù)的參數(shù)的 length。

抽象地講,詞法環(huán)境在偽代碼中看起來像這樣:

GlobalExectionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Object",
   // 在這里綁定標(biāo)識符
  }
  outer: <null>
 }
}

FunctionExectionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Declarative",
   // 在這里綁定標(biāo)識符
  }
  outer: <Global or outer function environment reference>
 }
}

變量環(huán)境:

它同樣是一個(gè)詞法環(huán)境,其環(huán)境記錄器持有變量聲明語句在執(zhí)行上下文中創(chuàng)建的綁定關(guān)系。

如上所述,變量環(huán)境也是一個(gè)詞法環(huán)境,所以它有著上面定義的詞法環(huán)境的所有屬性。

在 ES6 中,詞法環(huán)境組件和變量環(huán)境的一個(gè)不同就是前者被用來存儲函數(shù)聲明和變量(let 和 const)綁定,而后者只用來存儲 var 變量綁定。

我們看點(diǎn)樣例代碼來理解上面的概念:

let a = 20;
const b = 30;
var c;
function multiply(e, f) {
 var g = 20;
 return e * f * g;
}
c = multiply(20, 30);

執(zhí)行上下文看起來像這樣:

GlobalExectionContext = {

 ThisBinding: <Global Object>,

 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Object",
   // 在這里綁定標(biāo)識符
   a: < uninitialized >,
   b: < uninitialized >,
   multiply: < func >
  }
  outer: <null>
 },

 VariableEnvironment: {
  EnvironmentRecord: {
   Type: "Object",
   // 在這里綁定標(biāo)識符
   c: undefined,
  }
  outer: <null>
 }
}

FunctionExectionContext = {
 ThisBinding: <Global Object>,

 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Declarative",
   // 在這里綁定標(biāo)識符
   Arguments: {0: 20, 1: 30, length: 2},
  },
  outer: <GlobalLexicalEnvironment>
 },

VariableEnvironment: {
  EnvironmentRecord: {
   Type: "Declarative",
   // 在這里綁定標(biāo)識符
   g: undefined
  },
  outer: <GlobalLexicalEnvironment>
 }
}

注意 — 只有遇到調(diào)用函數(shù) multiply 時(shí),函數(shù)執(zhí)行上下文才會被創(chuàng)建。

可能你已經(jīng)注意到 let 和 const 定義的變量并沒有關(guān)聯(lián)任何值,但 var 定義的變量被設(shè)成了 undefined。

這是因?yàn)樵趧?chuàng)建階段時(shí),引擎檢查代碼找出變量和函數(shù)聲明,雖然函數(shù)聲明完全存儲在環(huán)境中,但是變量最初設(shè)置為 undefined(var 情況下),或者未初始化(let 和 const 情況下)。

這就是為什么你可以在聲明之前訪問 var 定義的變量(雖然是 undefined),但是在聲明之前訪問 let 和 const 的變量會得到一個(gè)引用錯(cuò)誤。

這就是我們說的變量聲明提升。

執(zhí)行階段

這是整篇文章中最簡單的部分。在此階段,完成對所有這些變量的分配,最后執(zhí)行代碼。

注意 — 在執(zhí)行階段,如果 JavaScript 引擎不能在源碼中聲明的實(shí)際位置找到 let 變量的值,它會被賦值為 undefined。

“JavaScript中的執(zhí)行上下文和執(zhí)行棧的概念”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

分享題目:JavaScript中的執(zhí)行上下文和執(zhí)行棧的概念-創(chuàng)新互聯(lián)
標(biāo)題來源:http://muchs.cn/article28/deoojp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、標(biāo)簽優(yōu)化、品牌網(wǎng)站制作、動態(tài)網(wǎng)站、網(wǎng)站策劃、網(wǎng)站設(shè)計(jì)公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管