成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

javascript的執(zhí)行上下文

yedf / 2962人閱讀

摘要:運(yùn)行執(zhí)行上下文正在使用的執(zhí)行上下文。頂部是正在執(zhí)行的上下文當(dāng)執(zhí)行完畢,它的執(zhí)行上下文自動(dòng)從棧彈出,控制流程按順序到達(dá)全局執(zhí)行上下文。一旦所有代碼執(zhí)行完畢,引擎從當(dāng)前棧中移除全局執(zhí)行上下文。在全局執(zhí)行上下文中,的值指向全局對(duì)象。

https://juejin.im/post/5ba321...
https://juejin.im/entry/59986...
我只是搬運(yùn)工,看了他們的文章后深有啟發(fā),于是把他們的精華匯總?cè)缓蠹尤胱约旱睦斫庹砹诉@一篇文章。

這是一個(gè)非常抽象的概念,你無(wú)需徹底的弄明白它的意思,你只需要明白它做了什么。

在充分理解他做了什么之前還是要了解一下它到底是什么

Execution Context(執(zhí)行上下文)是 ECMA-262 標(biāo)準(zhǔn)中定義的一個(gè)抽象概念,用于同 Executable
Code(可執(zhí)行代碼)進(jìn)行區(qū)分。

1:什么是執(zhí)行代碼----Executable Code

合法的,可以被解釋器解析執(zhí)行的代碼。

分為三類

Global Code:全局代碼

Function Code:函數(shù)體內(nèi)的代碼

Eval Code:使用 eval() 函數(shù)執(zhí)行的代碼

2:什么是執(zhí)行上下文----Execution Context

執(zhí)行上下文 是 ES 用來(lái)跟蹤代碼運(yùn)行狀態(tài)和相關(guān)資源集合的特殊機(jī)制。它決定了執(zhí)行代碼執(zhí)行的過(guò)程中可以訪問(wèn)的數(shù)據(jù)。

每當(dāng) Javascript 代碼在運(yùn)行的時(shí)候,它都是在執(zhí)行上下文中運(yùn)行。

分為三類

Global Execution Context:全局執(zhí)行上下文

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

Function Execution Context:函數(shù)執(zhí)行上下文

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

Eval Execution Context:eval() 函數(shù)執(zhí)行上下文

由于 JavaScript 開(kāi)發(fā)者并不經(jīng)常使用 eval,所以在這里我不會(huì)討論它。

3:執(zhí)行上下文的基本工作方式

先理解兩個(gè)名詞:執(zhí)行上下文棧(Execution Context Stack)、運(yùn)行執(zhí)行上下文(Running Execution Context)

執(zhí)行上下文棧( Execution Context Stack ):用來(lái)保存所有執(zhí)行上下文的棧,是一種擁有 LIFO(后進(jìn)先出)數(shù)據(jù)結(jié)構(gòu)的棧。 當(dāng) JavaScript 引擎第一次遇到你的腳本時(shí),它會(huì)創(chuàng)建一個(gè)全局的執(zhí)行上下文并且壓入當(dāng)前執(zhí)行棧。每當(dāng)引擎遇到一個(gè)函數(shù)調(diào)用,它會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并壓入棧的頂部。引擎會(huì)執(zhí)行那些執(zhí)行上下文位于棧頂?shù)暮瘮?shù)。當(dāng)該函數(shù)執(zhí)行結(jié)束時(shí),執(zhí)行上下文從棧中彈出,控制流程到達(dá)當(dāng)前棧中的下一個(gè)上下文。
運(yùn)行執(zhí)行上下文( Running Execution Context ):正在使用的執(zhí)行上下文。在任意時(shí)間,最多只能有一
個(gè)正在運(yùn)行代碼的執(zhí)行上下文。

4:基本工作方式

運(yùn)行執(zhí)行上下文總是在執(zhí)行上下文棧的頂部,全局執(zhí)行上下文總在執(zhí)行上下文棧的底部。無(wú)論什么時(shí)候,只要控制權(quán)從與當(dāng)前運(yùn)行執(zhí)行上下文相關(guān)的可執(zhí)行代碼上切換到另一部分與當(dāng)前運(yùn)行執(zhí)行上下文不相關(guān)的可執(zhí)行代碼上,一個(gè)新的執(zhí)行上下文就會(huì)被創(chuàng)建,新創(chuàng)建的執(zhí)行上下文會(huì)被放在當(dāng)前的運(yùn)行執(zhí)行上下文的上面,成為新的運(yùn)行執(zhí)行上下文。

5:具體工作流程

如前言中提到的,ES 標(biāo)準(zhǔn)中并沒(méi)有從技術(shù)實(shí)現(xiàn)的角度定義執(zhí)行上下文準(zhǔn)確類型和結(jié)構(gòu),為了更方便地解釋
執(zhí)行代碼和執(zhí)行上下文之間的關(guān)系,暫且用數(shù)組表示執(zhí)行上下文棧,然后用偽代碼來(lái)操作執(zhí)行上下文棧:
DCStack = []   // 執(zhí)行上下文棧

<1:開(kāi)始執(zhí)行代碼:全局執(zhí)行代碼與全局執(zhí)行上下文

解析器在解析執(zhí)行代碼時(shí)首先執(zhí)行全局代碼,為其創(chuàng)建對(duì)應(yīng)的執(zhí)行上下文,全局上下文被壓入執(zhí)行上下文棧
ECStack = [
  globalContext   // 全局執(zhí)行上下文
]

<2:開(kāi)始執(zhí)行函數(shù):函數(shù)代碼與函數(shù)執(zhí)行上下文

注意:函數(shù)代碼中不包括內(nèi)部函數(shù)的代碼
運(yùn)行下面的函數(shù)

(function foo(bar) {
   if (bar) {
     return
   }
   foo(true);
})()

我們用偽代碼還原一下執(zhí)行棧中發(fā)生了什么??

// 第一次調(diào)用 foo

ECStack = [
   functionContext,
  globalContext
]

// 第二次調(diào)用 foo

ECStack = [
   functionContext – recursively(遞歸),
   functionContext,
  globalContext
]

我們看一個(gè)實(shí)際的例子

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í)行這段代碼,解析器解析到了這段代碼,于是先創(chuàng)建了一個(gè)全局上下文,并把全局上下文壓入執(zhí)行棧

ECStack = [
    Global Context
]

然后解析器檢測(cè)到了 first(),開(kāi)始調(diào)用first函數(shù),于是創(chuàng)建了一個(gè)first函數(shù)上下文,并把這個(gè)函數(shù)向下文壓入到執(zhí)行棧的頂部(一般執(zhí)行棧的頂部都是正在運(yùn)行的上下文,現(xiàn)在正在調(diào)用first函數(shù),所以頂部就是他的上下文)

ECSstack= [
    First Function Context-----(頂部是正在執(zhí)行的上下文)
    Global Context
]

在first() 函數(shù)內(nèi)部又調(diào)用了second()函數(shù),于是JavaScript 引擎為second()函數(shù)創(chuàng)建了一個(gè)屬于他的執(zhí)行上下文,并把它壓入執(zhí)行棧的最頂部。(因?yàn)楝F(xiàn)在執(zhí)行second()函數(shù),所以他的執(zhí)行上下文就在最頂部,因?yàn)閒irst()函數(shù)沒(méi)有執(zhí)行完所以他的執(zhí)行上下文依然在執(zhí)行棧的隊(duì)列中)

ECSstack = [
    Cecond Function Context-----(頂部是正在執(zhí)行的上下文)
    First Function Context
    Global Context
]

執(zhí)行完second()函數(shù)之后,它的執(zhí)行上下文會(huì)自動(dòng)從執(zhí)行棧彈出,并且控制流程執(zhí)行下一個(gè)執(zhí)行上下文,即 first() 函數(shù)的執(zhí)行上下文。

ECSstack= [
       First Function Context-----(頂部是正在執(zhí)行的上下文)
       Global Context
]

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

ECStack = [
    Global Context
]

6:JavaScript 引擎是怎么創(chuàng)建執(zhí)行上下文?

創(chuàng)建執(zhí)行上下文有兩個(gè)階段:1>:創(chuàng)建階段 和 2>:執(zhí)行階段。

1>:創(chuàng)建階段--(The Creation Phase)

在創(chuàng)建階段會(huì)發(fā)生三件事

 ExecutionContext = {
      ThisBinding = ,  // this綁定
      LexicalEnvironment = { ... },  // 詞法環(huán)境
      VariableEnvironment = { ... }, // 變量環(huán)境
    }

This 綁定。

在全局執(zhí)行上下文中,this 的值指向全局對(duì)象。(在瀏覽器中,this引用 Window 對(duì)象)。在函數(shù)執(zhí)行上下文中,this 的值取決于該函數(shù)是如何被調(diào)用的。如果它被一個(gè)引用對(duì)象調(diào)用,那么 this 會(huì)被設(shè)置成那個(gè)對(duì)象,否則 this 的值被設(shè)置為全局對(duì)象或者 undefined(在嚴(yán)格模式下)。例如:
let foo = {
  baz: function() {
      console.log(this);
  }
}
foo.baz();   // "this" 引用 "foo", 因?yàn)?"baz" 被對(duì)象 "foo" 調(diào)用

let bar = foo.baz;

bar();       // "this" 指向全局 window 對(duì)象,因?yàn)闆](méi)有指定引用對(duì)象

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

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

有點(diǎn)沒(méi)明白

簡(jiǎn)單來(lái)說(shuō)詞法環(huán)境是一種定義標(biāo)識(shí)符以及變量的嵌套結(jié)構(gòu)。(這里的標(biāo)識(shí)符指的是變量/函數(shù)的名字,而變量是對(duì)實(shí)際對(duì)象[包含函數(shù)類型對(duì)象]或原始數(shù)據(jù)的引用)。

在詞法環(huán)境的內(nèi)部有兩個(gè)部件組成:

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

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

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

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

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

環(huán)境記錄器也有兩種類型:

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

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

簡(jiǎn)而言之,

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

注意

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

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

GlobalExectionContext = {    // 全局執(zhí)行上下文
  LexicalEnvironment: {      // 詞法環(huán)境組件
    EnvironmentRecord: {    // 環(huán)境記錄器 ---對(duì)象環(huán)境記錄器
      Type: "Object",
      // 在這里綁定標(biāo)識(shí)符
    }
    outer:    // 外部環(huán)境引用,    是沒(méi)有外部環(huán)境引用的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用是 null。
  }
}

FunctionExectionContext = {    // 函數(shù)執(zhí)行上下文 
  LexicalEnvironment: {      // 詞法環(huán)境組件
    EnvironmentRecord: {    // 環(huán)境記錄器 ---聲明式環(huán)境記錄器
      Type: "Declarative",
      // 在這里綁定標(biāo)識(shí)符
    }
    outer:   //外部環(huán)境引用   函數(shù)內(nèi)部用戶定義的變量存儲(chǔ)在環(huán)境記錄器中。并且引用的外部環(huán)境可能是全局環(huán)境,或者任何包含此內(nèi)部函數(shù)的外部函數(shù)。
  }
}


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

變量環(huán)境也是一個(gè)詞法環(huán)境。所以它有著上面定義的詞法環(huán)境的所有屬性,其環(huán)境記錄器持有變量聲明語(yǔ)句在執(zhí)行上下文中創(chuàng)建的綁定關(guān)系。
在 ES6 中,詞法環(huán)境組件變量環(huán)境組件的一個(gè)不同就是前者被用來(lái)存儲(chǔ)函數(shù)聲明和變量(let 和 const)綁定,而后者只用來(lái)存儲(chǔ) var 變量綁定

來(lái)個(gè)栗子

let a = 20;
const b = 30;
var c;

function multiply(e, f) {
 var g = 20;
 return e * f * g;
}

c = multiply(20, 30);

執(zhí)行上下文用偽函數(shù)這么表示

// 全局執(zhí)行上下文
GlobalExectionContext = {  
    
 1:ThisBinding: ,    //this綁定

 2: LexicalEnvironment: {  // 詞法環(huán)境  --全局的詞法環(huán)境
    EnvironmentRecord: {  //環(huán)境記錄器
      Type: "Object",  
      // 在這里綁定標(biāo)識(shí)符
      a: < uninitialized >,  // 變量a的綁定(let)
      b: < uninitialized >,  // 變量b 的綁定(const)
      multiply: < func >   // 函數(shù)聲明
    }
    outer:   // 外部環(huán)境的引用nul
  },

3: VariableEnvironment: {  // 變量環(huán)境 --全局的詞法環(huán)境
    EnvironmentRecord: {  //環(huán)境記錄器
      Type: "Object",
      // 在這里綁定標(biāo)識(shí)符
      c: undefined,    // 變量c 的綁定(var)
    }
    outer:     // 外部環(huán)境的引用nul
  }
}

// 函數(shù)的執(zhí)行上下文-----(只有遇到調(diào)用函數(shù) multiply 時(shí),函數(shù)執(zhí)行上下文才會(huì)被創(chuàng)建)
FunctionExectionContext = { 

  1:ThisBinding: ,   // this 綁定 

  2:LexicalEnvironment: {  //詞法環(huán)境  --函數(shù)的詞法環(huán)境
    EnvironmentRecord: {  // 環(huán)境記錄器
      Type: "Declarative",
      // 在這里綁定標(biāo)識(shí)符
      Arguments: {0: 20, 1: 30, length: 2},   // 聲明式環(huán)境記錄器還包含了一個(gè)傳遞給函數(shù)的 arguments 對(duì)象(此對(duì)象存儲(chǔ)函數(shù)參數(shù)鍵值對(duì)和傳遞給函數(shù)的參數(shù)的length)。
    },
    outer:   // 外部環(huán)境的引用是全局環(huán)境
  },

 3:VariableEnvironment: {  //變量環(huán)境
    EnvironmentRecord: {  // 環(huán)境記錄器
      Type: "Declarative",
      // 在這里綁定標(biāo)識(shí)符
      g: undefined   // 變量g的綁定(var)
    },
    outer:    // 外部環(huán)境的引用是全局環(huán)境
  }
}
可能你已經(jīng)注意到 let 和 const 定義的變量并沒(méi)有關(guān)聯(lián)任何值,但 var 定義的變量被設(shè)成了 undefined。
這是因?yàn)樵趧?chuàng)建階段時(shí),引擎檢查代碼找出變量和函數(shù)聲明,雖然函數(shù)聲明完全存儲(chǔ)在環(huán)境中,但是變量最初設(shè)置為 undefined(var
情況下),或者未初始化(let 和 const 情況下)。 這就是為什么你可以在聲明之前訪問(wèn) var 定義的變量(雖然是
undefined),但是在聲明之前訪問(wèn) let 和 const 的變量會(huì)得到一個(gè)引用錯(cuò)誤。 這就是我們說(shuō)的變量聲明提升。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/98291.html

相關(guān)文章

  • 前端進(jìn)階系列(七):什么是執(zhí)行下文?什么是調(diào)用棧?

    摘要:什么是中的調(diào)用棧調(diào)用棧就像是程序當(dāng)前執(zhí)行的日志。當(dāng)函數(shù)執(zhí)行結(jié)束時(shí),將從調(diào)用棧中出去。了解全局和局部執(zhí)行上下文是掌握作用域和閉包的關(guān)鍵??偨Y(jié)引擎創(chuàng)建執(zhí)行上下文,全局存儲(chǔ)器和調(diào)用棧。 原文作者:Valentino 原文鏈接:https://www.valentinog.com/blog/js-execution-context-call-stack 什么是Javascript中的執(zhí)行上下文...

    leone 評(píng)論0 收藏0
  • JavaScript深入之執(zhí)行下文

    摘要:深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的為例,具體講解當(dāng)函數(shù)執(zhí)行的時(shí)候,執(zhí)行上下文棧變量對(duì)象作用域鏈?zhǔn)侨绾巫兓?。前言在深入之?zhí)行上下文棧中講到,當(dāng)代碼執(zhí)行一段可執(zhí)行代碼時(shí),會(huì)創(chuàng)建對(duì)應(yīng)的執(zhí)行上下文。 JavaScript深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的demo為例,具體講解當(dāng)函數(shù)執(zhí)行的時(shí)候,執(zhí)行上下文棧、變量對(duì)象、作用域鏈?zhǔn)侨绾巫兓摹?前言 在《Jav...

    gougoujiang 評(píng)論0 收藏0
  • JavaScript深入之執(zhí)行下文

    摘要:深入系列第三篇,講解執(zhí)行上下文棧的是如何執(zhí)行的,也回答了第二篇中的略難的思考題。 JavaScript深入系列第三篇,講解執(zhí)行上下文棧的是如何執(zhí)行的,也回答了第二篇中的略難的思考題。 順序執(zhí)行? 如果要問(wèn)到 JavaScript 代碼執(zhí)行順序的話,想必寫過(guò) JavaScript 的開(kāi)發(fā)者都會(huì)有個(gè)直觀的印象,那就是順序執(zhí)行,畢竟: var foo = function () { ...

    codecraft 評(píng)論0 收藏0
  • 「JS篇」JavaScript 執(zhí)行下文和提升

    摘要:執(zhí)行上下文當(dāng)代碼運(yùn)行的時(shí)候,運(yùn)行代碼的環(huán)境形成了執(zhí)行上下文,執(zhí)行上下文決定代碼可以訪問(wèn)哪些變量函數(shù)對(duì)象等。我們將執(zhí)行上下文簡(jiǎn)單視為運(yùn)行當(dāng)前代碼的,我們知道作用域分為和。完成后,其執(zhí)行堆棧將從堆棧中刪除,將控制權(quán)交給全局執(zhí)行上下文。 我們通常將 JavaScript 歸類為動(dòng)態(tài)或解釋執(zhí)行語(yǔ)言,但實(shí)際上它也是一門編譯語(yǔ)言,它有自己的編譯器形式,運(yùn)行在 JavaScript 引擎中。 每個(gè) ...

    googollee 評(píng)論0 收藏0
  • JavaScript深入之閉包

    摘要:深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。定義對(duì)閉包的定義為閉包是指那些能夠訪問(wèn)自由變量的函數(shù)。 JavaScript深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。 定義 MDN 對(duì)閉包的定義為: 閉包是指那些能夠訪問(wèn)自由變量的函數(shù)。 那什么是自由變量呢? 自由變量是指在函數(shù)中使用的,但既不是函數(shù)參數(shù)也...

    caige 評(píng)論0 收藏0
  • 深入理解JavaScript執(zhí)行下文執(zhí)行

    摘要:執(zhí)行上下文和執(zhí)行棧是中關(guān)鍵概念之一,是難點(diǎn)之一。理解執(zhí)行上下文和執(zhí)行棧同樣有助于理解其他的概念如提升機(jī)制作用域和閉包等。函數(shù)執(zhí)行完成,函數(shù)的執(zhí)行上下文出棧,并且被銷毀。 前言 如果你是一名 JavaScript 開(kāi)發(fā)者,或者想要成為一名 JavaScript 開(kāi)發(fā)者,那么你必須知道 JavaScript 程序內(nèi)部的執(zhí)行機(jī)制。執(zhí)行上下文和執(zhí)行棧是JavaScript中關(guān)鍵概念之一,是Ja...

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

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

0條評(píng)論

閱讀需要支付1元查看
<