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

資訊專欄INFORMATION COLUMN

[譯]執(zhí)行上下文、作用域鏈和JS內(nèi)部機(jī)制

caozhijian / 1250人閱讀

摘要:執(zhí)行上下文作用域鏈和內(nèi)部機(jī)制一執(zhí)行上下文執(zhí)行上下文是代碼的執(zhí)行環(huán)境,它包括的值變量對(duì)象和函數(shù)。創(chuàng)建作用域鏈一旦可變對(duì)象創(chuàng)建完,引擎就開(kāi)始初始化作用域鏈。

執(zhí)行上下文、作用域鏈和JS內(nèi)部機(jī)制(Execution context, Scope chain and JavaScript internals)

一、執(zhí)行上下文
執(zhí)行上下文(Execution context EC)是js代碼的執(zhí)行環(huán)境,它包括this的值、變量、對(duì)象和函數(shù)。
js執(zhí)行上下文有3種類型
1. 全局執(zhí)行上下文(Global execution context GEC)

全局上下文是文件第一次加載到瀏覽器,js代碼開(kāi)始執(zhí)行的默認(rèn)執(zhí)行上下文。在瀏覽器環(huán)境中,嚴(yán)格模式下this的值為undefined,否則this的值為window對(duì)象。GEC只能有一個(gè)(因?yàn)閖s執(zhí)行的全局環(huán)境只能有一個(gè))。

2. 函數(shù)執(zhí)行上下文(Functional execution context FEC)

函數(shù)執(zhí)行時(shí)創(chuàng)建函數(shù)執(zhí)行上下文,每個(gè)函數(shù)都有自己的執(zhí)行上下文。FEC可以獲取到GEC中的內(nèi)容。當(dāng)在全局上下文中執(zhí)行代碼時(shí)js引擎發(fā)現(xiàn)一個(gè)函數(shù)調(diào)用,則創(chuàng)建一個(gè)函數(shù)執(zhí)行上下文。

3. Eval

執(zhí)行eval時(shí)創(chuàng)建

二、執(zhí)行上下文棧
執(zhí)行上下文棧Execution context stack (ECS)是執(zhí)行js代碼時(shí)創(chuàng)建的執(zhí)行棧結(jié)構(gòu)。GEC默認(rèn)在棧的最里層,當(dāng)js引擎發(fā)現(xiàn)一個(gè)函數(shù)調(diào)用,則創(chuàng)建這個(gè)函數(shù)的FEC并push進(jìn)棧,js引擎執(zhí)行棧頂上下文關(guān)聯(lián)的函數(shù),一旦函數(shù)執(zhí)行完,則將其FEC pop出棧,并往下執(zhí)行。

看個(gè)例子(動(dòng)圖插不了棧動(dòng)圖鏈接)

var a = 10;

function functionA() {

    console.log("Start function A");

    function functionB(){
        console.log("In function B");
    }

    functionB();

}

functionA();

console.log("GlobalContext");

當(dāng)上面的代碼在瀏覽器中加載時(shí),js引擎先將GEC push入ECS中,當(dāng)在GEC中調(diào)用functionA時(shí),functionA執(zhí)行上下文被push入棧,并開(kāi)始執(zhí)行functionA。

當(dāng)functionB在functionA中被調(diào)用時(shí),functionB的執(zhí)行上下文被push入棧,開(kāi)始執(zhí)行functionB,當(dāng)functionB中內(nèi)容執(zhí)行完,functionB執(zhí)行上下文被pop出棧,此時(shí)棧頂為functionA的執(zhí)行上下文,繼續(xù)執(zhí)行functionA的代碼,執(zhí)行完后pop出棧,棧頂為GEC。

最終執(zhí)行GEC中代碼,執(zhí)行完pop整個(gè)代碼結(jié)束。

上面討論了js引擎如何處理執(zhí)行上下文(push和pop),下面討論js引擎如何創(chuàng)建執(zhí)行上下文,這個(gè)過(guò)程分為兩個(gè)階段:創(chuàng)建階段和執(zhí)行階段。

三、創(chuàng)建執(zhí)行上下文 1. 創(chuàng)建階段(后面又叫編譯階段)
js引擎調(diào)用函數(shù),但函數(shù)還沒(méi)開(kāi)始執(zhí)行階段。

js引擎在這個(gè)階段對(duì)整個(gè)函數(shù)進(jìn)行一個(gè)編譯(compile the code),主要干了下面三件事:

(1) 創(chuàng)建Activation object 或 the variable object(后面就簡(jiǎn)稱它可變對(duì)象吧,不知道有沒(méi)有專業(yè)的中文名)

可變對(duì)象是包含所有變量、函數(shù)參數(shù)和內(nèi)部函數(shù)聲明信息的特殊對(duì)象,它是一個(gè)特殊對(duì)象且沒(méi)有__proto__屬性。

(2)創(chuàng)建作用域鏈

一旦可變對(duì)象創(chuàng)建完,js引擎就開(kāi)始初始化作用域鏈。作用域鏈?zhǔn)且粋€(gè)當(dāng)前函數(shù)所在的可變對(duì)象的列表,其中包括GEC的可變對(duì)象和當(dāng)前函數(shù)的可變對(duì)象。

(3)決定this的值

初始化this的值

下面通過(guò)一個(gè)例子進(jìn)行說(shuō)明

function funA (a, b) {
  var c = 3;
  
  var d = 2;
  
  d = function() {
    return a - b;
  }
}


funA(3, 2);

當(dāng)調(diào)用funA和執(zhí)行funA前的這段時(shí)間,js引擎為funA創(chuàng)建了一個(gè)executionContextObj如下

executionContextObj = {
 variableObject: {}, // All the variable, arguments and inner function details of the funA
 scopechain: [], // List of all the scopes inside which the current function is
 this // Value of this 
}

可變對(duì)象包含參數(shù)對(duì)象(包含函數(shù)參數(shù)的細(xì)節(jié)),聲明的變量和函數(shù),如下所示

variableObject = {
  argumentObject : {
    0: a,
    1: b,
    length: 2
  },
  a: 3,
  b: 2
  c: undefined,
  d: undefined then pointer to the function defintion of d
}

argumentObject如上所示

函數(shù)中的變量會(huì)被初始為undefined,參數(shù)也會(huì)在可變對(duì)象中呈現(xiàn)

如果變量在參數(shù)對(duì)象中已存在,js引擎選擇忽略

js引擎在當(dāng)前函數(shù)中遇到函數(shù)定義,會(huì)用函數(shù)名創(chuàng)建一個(gè)屬性指向函數(shù)定義存儲(chǔ)的堆內(nèi)容

2. 執(zhí)行階段
在此階段,js引擎會(huì)重掃一遍函數(shù),用具體的變量的值來(lái)更新可變對(duì)象,并執(zhí)行代碼內(nèi)容。

執(zhí)行階段執(zhí)行完后,可變對(duì)象的值如下:

variableObject = {
  argumentObject : {
    0: a,
    1: b,
    length: 2
  },
  a: 3,
  b: 2,
  c: 3,
  d: undefined then pointer to the function defintion of d
}
四、完整的例子

代碼如下

a = 1;

var b = 2;

cFunc = function(e) {
  var c = 10;
  var d = 15;
  
  a = 3
  
  function dFunc() {
    var f = 5;
  }
  
  dFunc();
}

cFunc(10);
全局編譯階段

當(dāng)瀏覽器加載上面的代碼后,js引擎進(jìn)入編譯階段,只處理聲明,不處理值。下面走讀一遍代碼:

a被賦值1,但它并不是個(gè)變量或函數(shù)聲明,js引擎在編譯階段什么都不做;

b變量聲明初始化為undefined;

cFunc函數(shù)聲明初始化為undefined。

此時(shí)的

globalExecutionContextObj = {
  variableObject: { // 原文中有時(shí)用activationObj
      argumentObj : {
          length:0
      },
      b: undefined,
      cFunc: Pointer to the function definition
  },
  scopeChain: [GLobal execution context variable object],
  this: value of this
}
全局執(zhí)行階段

再接著上面,js引擎進(jìn)入執(zhí)行階段并再過(guò)一遍。此時(shí)將會(huì)更新變量名和執(zhí)行

js引擎發(fā)現(xiàn)可變對(duì)象中沒(méi)有a屬性,因此在GEC中添加a屬性,并初始化為1;

可變對(duì)象有b,直接更新b的值為2;

接著是函數(shù)聲明,不做任何事;

最后調(diào)用cFunc,js引擎再次進(jìn)入編譯階段創(chuàng)建一個(gè)cFunc的執(zhí)行上下文。

此時(shí)

globalExecutionContextObj = {
  variableObject: {
      argumentObj : {
          length:0
      },
      b: 2,
      cFunc: Pointer to the function definition,
      a: 1
  },
  scopeChain: [GLobal execution context variable object],
  this: value of this
}
cFunc的編譯階段

由于cFunc有個(gè)參數(shù)e,js引擎會(huì)在cFunc執(zhí)行上下文對(duì)象可變對(duì)象添加e屬性,并初始化為2

js引擎查看cFunc執(zhí)行上下文的可變對(duì)象沒(méi)有c,因此添加c,并初始化為undefined,d類似;

a = 3非聲明,跳過(guò);

函數(shù)聲明,創(chuàng)建dFunc屬性指向函數(shù)的堆空間;

對(duì)dFunc執(zhí)行語(yǔ)句忽略

此時(shí)

cFuncExecutionContextObj = {
  activationbj: {
      argumentObj : {
          0: e,
          length:1
      },
      e: 10,
      c: undefined,
      d: undefined
      dFunc: Pointer to the function definition,
  },
  scopeChain: [cFunc variable object, Global exection context variable object],
  this: value of this
}
cFunc的執(zhí)行階段

c和d獲取到初始化值;

a不是cFunc執(zhí)行上下文對(duì)象中的屬性,js引擎會(huì)在作用率鏈的幫助下轉(zhuǎn)到GEC(全局執(zhí)行上下文),查找a是否在GEC中。如果不存在,則會(huì)在當(dāng)前作用域創(chuàng)建并初始化它;如果GEC中有,則更新其值,這里會(huì)更新為3。js引擎只有在發(fā)現(xiàn)一個(gè)變量在當(dāng)前執(zhí)行上下文對(duì)象屬性中找不到時(shí)會(huì)跳轉(zhuǎn)到GEC中;

創(chuàng)建dFunc屬性并指向函數(shù)的堆內(nèi)存

cFuncExecutionContextObj = {
  activationbj: {
      argumentObj : {
          0: e,
          length:1
      },
      e: 10,
      c: 10,
      d: 15
      dFunc: Pointer to the function definition,
  },
  scopeChain: [cFunc variable object, Global exection context variable object],
  this: value of this
}

調(diào)用dFunc,js引擎再次進(jìn)入編譯階段,創(chuàng)建dFunc執(zhí)行上下文對(duì)象。
dFunc執(zhí)行上下文對(duì)象可以訪問(wèn)到cFunc和全局作用域中的所有變量和函數(shù);同樣cFunc可以訪問(wèn)到全局的,但不能訪問(wèn)dFunc中的;全局上下文對(duì)象不能訪問(wèn)cFunc和dFunc中的變量和對(duì)象。
有了上面的概念,對(duì)hoisting(變量提升)應(yīng)該更容易理解了。

五、作用域鏈
作用域鏈?zhǔn)钱?dāng)前函數(shù)所在的可變對(duì)象列表

看下面一段代碼

a = 1;

var b = 2;

cFunc = function(e) {
  var c = 10;
  var d = 15;
  
  console.log(c);
  console.log(a); 
  
  function dFunc() {
    var f = 5;
    console.log(f)
    console.log(c);
    console.log(a); 
  }
  
  dFunc();
}

cFunc(10);

當(dāng)cFunc被調(diào)用時(shí),cFunc的作用域鏈如下

Scope chain of cFunc = [ cFunc variable object, 
                     Global Execution Context variable object]

當(dāng)dFunc被調(diào)用時(shí),dFunc在cFunc中,dFunc的作用域鏈包含dFunc、cFunc和全局可變對(duì)象

Scope chain of dFunc = [dFunc variable object, 
                    cFunc variable object,
                    Global execution context variable object]

當(dāng)我們嘗試訪問(wèn)dFunc中的f,js引擎查看f是否可從dFunc的可變對(duì)象中獲取,找到console輸出;
訪問(wèn)c變量,js引擎首先在dFunc的可變對(duì)象中獲取,不能獲取,則到cFunc的可變對(duì)象中去獲取,找到console輸出;
訪問(wèn)a變量,同上,最后找到GEC的可變對(duì)象,獲取到并console輸出

同樣,cFunc中獲取c和a類似

在cFunc中訪問(wèn)不到f變量,但dFunc中可以通過(guò)作用域鏈獲取到c和d

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

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

相關(guān)文章

  • JS基礎(chǔ)知識(shí):變量對(duì)象、作用鏈和閉包

    摘要:前言這段時(shí)間一直在消化作用域鏈和閉包的相關(guān)知識(shí)。而作用域鏈則是這套規(guī)則這套規(guī)則的具體運(yùn)行。是變量對(duì)象的縮寫(xiě)那這樣放有什么好處呢我們知道作用域鏈保證了當(dāng)前執(zhí)行環(huán)境對(duì)符合訪問(wèn)權(quán)限的變量和函數(shù)的有序訪問(wèn)。 前言:這段時(shí)間一直在消化作用域鏈和閉包的相關(guān)知識(shí)。之前看《JS高程》和一些技術(shù)博客,對(duì)于這些概念的論述多多少少不太清楚或者不太完整,包括一些大神的技術(shù)文章。這也給我的學(xué)習(xí)上造成了一些困惑,...

    Keven 評(píng)論0 收藏0
  • 【進(jìn)階2-1期】深入淺出圖解作用鏈和閉包

    摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問(wèn)外鏈,點(diǎn)擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對(duì)象,在全局環(huán)境中定義的變量就會(huì)綁定到全局對(duì)象中。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第6天。 本...

    levius 評(píng)論0 收藏0
  • JS 作用域鏈

    摘要:首先,在創(chuàng)建函數(shù)時(shí),作用域鏈內(nèi)就會(huì)先填入對(duì)象,圖片只例舉了全部變量中的一部分。然后,解釋器進(jìn)入函數(shù)的執(zhí)行環(huán)境,同樣的,首先填入父級(jí)的作用域鏈,就是的,包括了對(duì)象活動(dòng)對(duì)象。之后再把的活動(dòng)對(duì)象填入到作用域鏈最頂部,這就是的作用域鏈了。 之前學(xué)習(xí)JS函數(shù)部分時(shí),提到了作用域這一節(jié),但是因?yàn)槭褂貌牧蠒?shū)不同,今天在讀博客的時(shí)候發(fā)現(xiàn)其實(shí)還有一個(gè)知識(shí)點(diǎn)即作用域鏈,所以來(lái)寫(xiě)一些個(gè)人理解和認(rèn)識(shí)加深記憶。...

    darry 評(píng)論0 收藏0
  • 【進(jìn)階2-3期】JavaScript深入之閉包面試題解

    摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個(gè)值,這引起的一個(gè)副作用就是如果內(nèi)部函數(shù)在一個(gè)循環(huán)中,那么變量的值始終為最后一個(gè)值。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了...

    alanoddsoff 評(píng)論0 收藏0
  • 【進(jìn)階2-2期】JavaScript深入之從作用域鏈理解閉包

    摘要:使用上一篇文章的例子來(lái)說(shuō)明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問(wèn)外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)...

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

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

0條評(píng)論

閱讀需要支付1元查看
<