成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入理解Javascript中的執(zhí)行環(huán)境(Execution Context)和執(zhí)行棧(Execut

whidy / 1674人閱讀

摘要:引擎會(huì)執(zhí)行其執(zhí)行環(huán)境位于堆棧頂部的函數(shù)。當(dāng)函數(shù)執(zhí)行完畢時(shí),當(dāng)前執(zhí)行棧會(huì)從堆棧中彈出去,并且控件將會(huì)到達(dá)其在當(dāng)前堆棧下面的那個(gè)執(zhí)行環(huán)境中。當(dāng)完成以后,它的執(zhí)行環(huán)境會(huì)會(huì)從堆棧中移出,并且控件會(huì)到達(dá)全局執(zhí)行環(huán)境。

如果你想成為一個(gè)Javascript開(kāi)發(fā)者,那么你一定要知道Javascript程序的內(nèi)部運(yùn)行原理。理解執(zhí)行環(huán)境和執(zhí)行棧是非常重要的,其有助于理解其他Javascript的概念,比如說(shuō)提升,作用域和閉包等。

當(dāng)然,理解執(zhí)行環(huán)境和執(zhí)行棧的概念也將會(huì)使你成為一個(gè)更好的Javascript開(kāi)發(fā)者。

閑話少說(shuō),馬上開(kāi)始吧。

執(zhí)行環(huán)境是什么

簡(jiǎn)單來(lái)說(shuō),執(zhí)行環(huán)境就是Javascript代碼被計(jì)算和執(zhí)行的環(huán)境的一個(gè)抽象概念。無(wú)論Javascript代碼在什么時(shí)候運(yùn)行,它都會(huì)運(yùn)行在 執(zhí)行環(huán)境中。

執(zhí)行環(huán)境的類型

在Javascript中有三種執(zhí)行環(huán)境的類型。

全局執(zhí)行環(huán)境 - 這是一種默認(rèn)和基礎(chǔ)的執(zhí)行環(huán)境。如果代碼不在任何的函數(shù)中,那么它就是在全局執(zhí)行環(huán)境中。他做了兩件事情:首先,它創(chuàng)建了一個(gè)全局對(duì)象 - windows(如果是瀏覽器的話),并且把this的值設(shè)置到全局對(duì)象中。在程序中,只會(huì)存在一個(gè)全局執(zhí)行環(huán)境。

函數(shù)執(zhí)行環(huán)境 - 每次當(dāng)函數(shù)被調(diào)用的時(shí)候,就會(huì)為該函數(shù)創(chuàng)建一個(gè)全新的執(zhí)行環(huán)境。每個(gè)函數(shù)都有他們自己的執(zhí)行環(huán)境,但是他們僅僅是在函數(shù)被調(diào)用的時(shí)候才會(huì)被創(chuàng)建。其可以有任意多個(gè)函數(shù)執(zhí)行環(huán)境。無(wú)論新的執(zhí)行環(huán)境在什么時(shí)候被創(chuàng)建,它都會(huì)按照定義的順序依次執(zhí)行一系列的步驟,不過(guò)這些我們稍后會(huì)講。

eval函數(shù)執(zhí)行環(huán)境 - 在eval函數(shù)中執(zhí)行代碼也會(huì)獲得它自己的執(zhí)行環(huán)境,但是eval并不經(jīng)常被Javascript開(kāi)發(fā)者所使用,所以這里我們目前并不打算討論它。

執(zhí)行棧

執(zhí)行棧,在其他編程語(yǔ)言中也被稱為調(diào)用棧,它是一種LIFO(后進(jìn)先出)的結(jié)構(gòu),被用于在代碼執(zhí)行階段存儲(chǔ)所有創(chuàng)建過(guò)的執(zhí)行環(huán)境。

當(dāng)Javascript引擎首次運(yùn)行到你的腳本時(shí),它會(huì)創(chuàng)建一個(gè)全局執(zhí)行環(huán)境,并把它推入到當(dāng)前的執(zhí)行棧中。每當(dāng)引擎運(yùn)行到其函數(shù)調(diào)用時(shí),就會(huì)為這個(gè)函數(shù)創(chuàng)建一個(gè)新的執(zhí)行環(huán)境,并把它推入到堆棧的頂部。

引擎會(huì)執(zhí)行其執(zhí)行環(huán)境位于堆棧頂部的函數(shù)。當(dāng)函數(shù)執(zhí)行完畢時(shí),當(dāng)前執(zhí)行棧會(huì)從堆棧中彈出去,并且控件將會(huì)到達(dá)其在當(dāng)前堆棧下面的那個(gè)執(zhí)行環(huán)境中。

我們來(lái)通過(guò)下面的代碼示例來(lái)理解:

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");

當(dāng)上面的代碼加載到瀏覽器中時(shí),Javascript引擎會(huì)創(chuàng)建一個(gè)全局執(zhí)行環(huán)境,并把它推到當(dāng)前的執(zhí)行棧中。當(dāng)遇到對(duì)first()的調(diào)用時(shí),Javascript引擎會(huì)為這個(gè)函數(shù)創(chuàng)建一個(gè)新的執(zhí)行環(huán)境,并且把它推到當(dāng)前執(zhí)行棧的頂部。

當(dāng)second()函數(shù)在first()函數(shù)內(nèi)被調(diào)用時(shí),Javascript引擎會(huì)為這個(gè)函數(shù)創(chuàng)建一個(gè)新的執(zhí)行環(huán)境,并把它推送到當(dāng)前執(zhí)行棧的頂部。當(dāng)second()函數(shù)完成的時(shí)候,它的執(zhí)行環(huán)境會(huì)從當(dāng)前的棧中推出去,并且空間會(huì)到達(dá)當(dāng)前環(huán)境下面的那個(gè)執(zhí)行環(huán)境中,也就是first()函數(shù)執(zhí)行環(huán)境。

當(dāng)first()完成以后,它的執(zhí)行環(huán)境會(huì)會(huì)從堆棧中移出,并且控件會(huì)到達(dá)全局執(zhí)行環(huán)境。當(dāng)所有代碼執(zhí)行完以后,Javascript引擎會(huì)從當(dāng)前棧中移出全局執(zhí)行環(huán)境。

那么執(zhí)行環(huán)境是如何被創(chuàng)建出來(lái)的呢?

到現(xiàn)在為止,我們已經(jīng)看到Javascript引擎是如何管理執(zhí)行環(huán)境的。那么現(xiàn)在咱們來(lái)理解一下執(zhí)行環(huán)境是如何被Javascript引擎創(chuàng)建出來(lái)的吧。

執(zhí)行環(huán)境的創(chuàng)建過(guò)程分為兩個(gè)階段:1,創(chuàng)建階段,2,執(zhí)行階段。

創(chuàng)建階段

執(zhí)行環(huán)境是在創(chuàng)建階段被創(chuàng)建出來(lái)的。在創(chuàng)建階段會(huì)發(fā)生下面的事情:

詞法環(huán)境組件被創(chuàng)建出來(lái)。

變量環(huán)境組件被創(chuàng)建出來(lái)。

因此執(zhí)行環(huán)境從概念上可以被表示為:

ExecutionContext = {
  LexicalEnvironment = ,
  VariableEnvironment = ,
}


詞法環(huán)境

官方ES6文檔定義的詞法環(huán)境如下:

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

簡(jiǎn)單來(lái)說(shuō),詞法環(huán)境是一個(gè)保存“變量-標(biāo)識(shí)符”映射的結(jié)構(gòu)。(標(biāo)識(shí)符指向變量/函數(shù)的名稱,變量是實(shí)際對(duì)象【包括函數(shù)對(duì)象和數(shù)組對(duì)象】的引用,或者是原始值)

例如,思考下面的代碼片段:

var a = 20;
var b = 40;
function foo() {
  console.log("bar");
}

上面的代碼片段的詞法環(huán)境如下:

lexicalEnvironment = {
  a: 20,
  b: 40,
  foo: 
}

每一個(gè)詞法環(huán)境都有三組件:

環(huán)境記錄

對(duì)外層環(huán)境的引用

this綁定

環(huán)境記錄

環(huán)境記錄是變量和函數(shù)聲明的地方,其被存儲(chǔ)在詞法環(huán)境內(nèi)部。

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

聲明環(huán)境記錄 - 顧名思義,它存儲(chǔ)變量和函數(shù)的聲明。函數(shù)代碼的詞法環(huán)境包含一個(gè)聲明環(huán)境記錄。

對(duì)象環(huán)境記錄 - 全局代碼的詞法環(huán)境包含一個(gè)對(duì)象環(huán)境記錄。除了變量和函數(shù)聲明之外,對(duì)象環(huán)境記錄也會(huì)存儲(chǔ)全局綁定對(duì)象(瀏覽器中的window對(duì)象)。因此對(duì)于每個(gè)綁定對(duì)象的屬性(對(duì)于瀏覽器,它包含所有由瀏覽器給window對(duì)象的屬性和方法),在記錄中創(chuàng)建一個(gè)新的條目。

注意 - 對(duì)于函數(shù)代碼,環(huán)境記錄也會(huì)包含參數(shù)對(duì)象,參數(shù)對(duì)象包含傳遞給函數(shù)的參數(shù)以及索引,和傳遞給函數(shù)的參數(shù)的長(zhǎng)度(個(gè)數(shù))。例如,下面函數(shù)的參數(shù)對(duì)象看起來(lái)像這樣子的:

function foo(a, b) {
  var c = a + b;
}
foo(2, 3);
// argument object
Arguments: {0: 2, 1: 3, length: 2},


對(duì)外部環(huán)境的引用

對(duì)外部環(huán)境的引用意味著它可以訪問(wèn)外面的詞法環(huán)境。這意味著如果他們?cè)诋?dāng)前的詞法環(huán)境中沒(méi)有找到的話,Javascript引擎會(huì)在外面的環(huán)境里去尋找變量。

this綁定

在這個(gè)組件中,this的值是確定的或者是已經(jīng)設(shè)置的。

在全局執(zhí)行環(huán)境中,this的值指向全局對(duì)象。(在瀏覽器中,this指向window對(duì)象)

在函數(shù)執(zhí)行環(huán)境中,this的值依賴于函數(shù)的調(diào)用方式。如果它是在對(duì)象引用中被調(diào)用,this的值就被設(shè)置為那個(gè)對(duì)象,否則,this的值會(huì)被設(shè)置為全局對(duì)象或者是undefined(在嚴(yán)格模式中)。例如:

const person = {
  name: "peter",
  birthYear: 1994,
  calcAge: function() {
    console.log(2018 - this.birthYear);
  }
}
person.calcAge();
// "this" refers to "person", because "calcAge" was called with //"person" object reference
const calculateAge = person.calcAge;
calculateAge();
// "this" refers to the global window object, because no object reference was given

抽象的說(shuō),在偽代碼中,詞法環(huán)境看起來(lái)像這樣:

GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
    }
    outer: ,
    this: 
  }
}
FunctionExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
    }
    outer: ,
    this: 
  }
}


變量環(huán)境:

它也是一個(gè)詞法環(huán)境,其環(huán)境記錄中環(huán)境記錄保存著在運(yùn)行環(huán)境中的VariableStatements創(chuàng)建的綁定。

正如上面所寫的,變量環(huán)境也是一個(gè)詞法環(huán)境,因此他有如上定義的詞法環(huán)境的所有的屬性和組件。

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

執(zhí)行階段

在這個(gè)階段,所有的變量賦值都會(huì)完成,所有的代碼最終也都會(huì)執(zhí)行完畢。

例子

我們來(lái)看一些例子來(lái)理解上面的概念。

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

當(dāng)上面的代碼被執(zhí)行的時(shí)候,Javascript引擎會(huì)創(chuàng)建一個(gè)全局的執(zhí)行環(huán)境來(lái)執(zhí)行這些全局代碼。因此全局執(zhí)行環(huán)境在創(chuàng)建階段看起來(lái)像這樣子的:

GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      a: < uninitialized >,
    b: < uninitialized >,
    multiply: < func >
  }
  outer: ,
    ThisBinding: 
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      c: undefined,
    }
    outer: , 
    ThisBinding: 
  }
}

在運(yùn)行階段,變量賦值已經(jīng)完成。因此全局執(zhí)行環(huán)境在執(zhí)行階段看起來(lái)就像是這樣的:

GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      a: 20,
      b: 30,
      multiply: < func >
    }
    outer: ,
    ThisBinding: 
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // Identifier bindings go here
      c: undefined,
    }
    outer: ,
    ThisBinding: 
  }
}

當(dāng)遇到函數(shù)multiply(20,30)的調(diào)用時(shí),一個(gè)新的函數(shù)執(zhí)行環(huán)境被創(chuàng)建并執(zhí)行函數(shù)中的代碼。因此函數(shù)執(zhí)行環(huán)境在創(chuàng)建階段看起來(lái)像是這樣子的:

FunctionExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
      Arguments: {0: 20, 1: 30, length: 2},
    },
    outer: ,
    ThisBinding: ,
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
      g: undefined
    },
    outer: ,
    ThisBinding: 
  }
}

在這以后,執(zhí)行環(huán)境會(huì)經(jīng)歷執(zhí)行階段,這意味著在函數(shù)內(nèi)部賦值給變量的過(guò)程已經(jīng)完成。因此此函數(shù)執(zhí)行環(huán)境在執(zhí)行階段看起來(lái)就像這樣的:

FunctionExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
      Arguments: {0: 20, 1: 30, length: 2},
    },
    outer: ,
    ThisBinding: ,
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
      g: 20
    },
    outer: ,
    ThisBinding: 
  }
}

在函數(shù)執(zhí)行完成以后,返回值會(huì)被存儲(chǔ)在c里。因此全局詞法環(huán)境被更新。在這之后,全局代碼執(zhí)行完成,程序運(yùn)行終止。

注意:正如你所注意到的,let和const在創(chuàng)建階段定義的變量沒(méi)有值與他們相關(guān)聯(lián),但是var定義變量會(huì)設(shè)置為false。

這是因?yàn)椋趧?chuàng)建階段,掃描代碼以查找變量和函數(shù)聲明,當(dāng)函數(shù)定義被全部存儲(chǔ)到環(huán)境中時(shí),變量首先會(huì)被初始化為undefined(在var的情況中),或者保持未初始化狀態(tài)(在let和const的情況中)。

這就是你在他們定義之前(雖然是undefined)訪問(wèn)var定義的變量,但是當(dāng)你在定義之前訪問(wèn)let和const定義的變量時(shí),會(huì)得到一個(gè)引用錯(cuò)誤。

這就是我們所謂的提升。

注意 - 在執(zhí)行階段,如果javascript引擎在源代碼中聲明的實(shí)際位置找不到let變量的值,那么它將為其分配未定義的值。

結(jié)論

所以我們已經(jīng)討論了如何在內(nèi)部執(zhí)行JavaScript程序。 雖然您沒(méi)有必要將所有這些概念都學(xué)習(xí)成為一名出色的JavaScript開(kāi)發(fā)人員,但對(duì)上述概念有一個(gè)正確的理解將有助于您更輕松,更深入地理解其他概念,如提升,作用域和閉包。

翻譯自:

https://blog.bitsrc.io/unders...

轉(zhuǎn)載自:http://www.lht.ren/article/18/

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101959.html

相關(guān)文章

  • 深入學(xué)習(xí)js之——執(zhí)行上下文

    摘要:當(dāng)遇到函數(shù)調(diào)用時(shí),引擎為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并把它壓入當(dāng)前執(zhí)行棧的頂部。參考鏈接理解中的執(zhí)行上下文和執(zhí)行棧深入之執(zhí)行上下文棧 開(kāi)篇 作為一個(gè)JavaScript的程序開(kāi)發(fā)者,如果被問(wèn)到JavaScript代碼的執(zhí)行順序,你腦海中是不是有一個(gè)直觀的印象 -- JavaScript 是順序執(zhí)行的,可事實(shí)真的是這樣的嗎? 讓我們首先看兩個(gè)小例子: var foo = functio...

    Lucky_Boy 評(píng)論0 收藏0
  • # JavaScript中的執(zhí)行上下文隊(duì)列()的關(guān)系?

    摘要:為什么會(huì)這樣這段代碼究竟是如何運(yùn)行的執(zhí)行上下文堆棧瀏覽器中的解釋器單線程運(yùn)行。瀏覽器始終執(zhí)行位于堆棧頂部的,并且一旦函數(shù)完成執(zhí)行當(dāng)前操作,它將從堆棧頂部彈出,將控制權(quán)返回到當(dāng)前堆棧中的下方上下文。確定在上下文中的值。 原文:What is the Execution Context & Stack in JavaScript? git地址:JavaScript中的執(zhí)行上下文和隊(duì)列(...

    DangoSky 評(píng)論0 收藏0
  • JavaScript基礎(chǔ)系列---執(zhí)行環(huán)境與作用域鏈

    摘要:延長(zhǎng)作用域鏈下面兩種語(yǔ)句可以在作用域鏈的前端臨時(shí)增加一個(gè)變量對(duì)象以延長(zhǎng)作用域鏈, 問(wèn)題 今天看筆記發(fā)現(xiàn)自己之前記了一個(gè)關(guān)于同名標(biāo)識(shí)符優(yōu)先級(jí)的內(nèi)容,具體是下面這樣的: 形參優(yōu)先級(jí)高于當(dāng)前函數(shù)名,低于內(nèi)部函數(shù)名 形參優(yōu)先級(jí)高于arguments 形參優(yōu)先級(jí)高于只聲明卻未賦值的局部變量,但是低于聲明且賦值的局部變量 函數(shù)和變量都會(huì)聲明提升,函數(shù)名和變量名同名時(shí),函數(shù)名的優(yōu)先級(jí)要高。執(zhí)行代...

    J4ck_Chan 評(píng)論0 收藏0
  • 深入理解JavaScript執(zhí)行上下文、函數(shù)堆、提升的概念

    摘要:原文鏈接變量對(duì)象是說(shuō)的執(zhí)行上下文中都有個(gè)對(duì)象用來(lái)存放執(zhí)行上下文中可被訪問(wèn)但是不能被的函數(shù)標(biāo)示符形參變量聲明等。對(duì)于函數(shù)的形參沒(méi)有什么可說(shuō)的,主要看一下函數(shù)的聲明以及變量的聲明兩個(gè)部分。 首先明確幾個(gè)概念: EC:函數(shù)執(zhí)行環(huán)境(或執(zhí)行上下文),Execution Context ECS:執(zhí)行環(huán)境棧,Execution Context Stack VO:變量對(duì)象,Variable Obj...

    hatlonely 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<