javascript 做用域鏈

  最近想整理一下js執行代碼的一些知識,若是有出錯的地方還請指正。javascript

  執行環境(Execution Context)java

  全部的javascript代碼都是在一個執行環境中被執行的。它只是一種機制,用來完成運行時做用域、生存期等方面的處理。緩存

  代碼分爲三種類型:閉包

  1.   Global Code 
  2.   Eval Code
  3.   Function Code 

  這是一個EC結構ide

能夠理解以下:函數

activeExecutionContext = {
    VO: {...}, // or AO
    this: thisValue,
    Scope: [ // Scope chain
      // 全部變量對象的列表
      // for identifiers lookup
    ]
};

 

  當一段程序開始時,會先進入到全局執行上下文環境。此時若是調用了某些函數,就會進入他們的上下文環境,執行完以後再退出該上下文環境。this

假設激活了一個EC1的上下文,圖解以下spa

  變量對象(VO)和活動對象(AO)code

  變量對象是一個與上下文相關的數據做用域,用於存儲被定義在上下文中的變量和函數聲明(不包括函數表達式)。在global全局上下文中,變量對象即便全局對象自身。對象

copy一個例子

var foo = 10;

function bar() {} // // 函數聲明
(function baz() {}); // 函數表達式

console.log(
  this.foo == foo, // true
  window.bar == bar // true
);

console.log(baz); // 引用錯誤,baz沒有被定義

全局上下文中的變量對象(VO)會有以下屬性:

在一個函數的上下文中,變量對象被表示爲活動對象

活動對象在進入上下文中初始化成了

AO = {
  arguments: <ArgO>
};

arguments屬性值就是Arguments對象

以後就是存儲當前上下文的變量與函數聲明

  做用域鏈

  做用域鏈是一個 對象列表,用於檢索上下文中出現的 標識符(變量名稱、函數聲明,普通參數)。

  在函數建立的時候,當前的做用域鏈被存儲到了函數的[[scope]]屬性中。當進入上下文建立AO/VO以後,上下文(EC)的Scope屬性做了以下處理:

Scope = AO|VO + [[Scope]]

 

  知識點就是這些,如今咱們來一段代碼,把過程給串聯起來。在這以前,咱們要知道另外一個知識,就是執行上下文的代碼被分紅兩個基本的階段來處理

  1. 進入執行上下文
  2. 執行代碼
var x = 10;
 
function foo(m) {
  var y = 20;
 
  function bar() {
    var z = 30;
    alert(x +  y + z + m);
  }
 
  bar();
}
 
foo(10); // 60

首先先進入全局上下文環境

  全局上下文的變量對象(代碼執行時,x才被賦值)是:

globalContext.VO === Global = {
  x: 10
  foo: <reference to function>
};

globalContext.Scope = globalContext.VO;

當"foo"建立時,

foo.[[Scope]] = [
  globalContext.Scope
];
//也就是
foo.[[Scope]] = [
  globalContext.VO
];

在"foo"激活時,進入了foo的上下文,foo上下文中的活動對象(代碼執行時,y才被賦值)是:

fooContext.AO = {
 arguments:<Arg>,
 m: 10,
 y:
20, bar: <reference to function> };

此時foo上下文中的做用域鏈爲:

fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.:
 
fooContext.Scope = [
  fooContext.AO,
  globalContext.VO
];

函數"bar"建立時,其[[scope]]爲:

bar.[[Scope]] = fooContext.Scope;
//也就是
bar.[[Scope]] = [ fooContext.AO, globalContext.VO ];

同理,bar就不寫了。最後bar上下文的做用域鏈爲:

barContext.Scope = barContext.AO + bar.[[Scope]] 
 
barContext.Scope = [
  barContext.AO,
  fooContext.AO,
  globalContext.VO
];

在alert執行時,做用域鏈中查找標示符以下

查找 x,  barContext.AO 無 ----> fooContext.AO 無 ---->globalContext.VO 有

查找 Z,  barContext.AO 有

其餘的就本身看了。

  局部變量、全局變量

  在做用域鏈中查找標識符是須要花時間的,因此就明白爲何須要儘可能使用局部變量(將頻繁使用的全局變量緩存下來),全局變量儘可能少用了。而且with會破壞做用域鏈,它會將指定的VO/AO加入到做用域鏈的頂端,這樣在標識符查找時,須要查找更長的做用域鏈。

  閉包

理解了上面的概念,閉包就很容易理解了。閉包是代碼塊和建立該代碼塊的上下文中數據的結合。

相關文章
相關標籤/搜索