最近想整理一下js執行代碼的一些知識,若是有出錯的地方還請指正。javascript
執行環境(Execution Context)java
全部的javascript代碼都是在一個執行環境中被執行的。它只是一種機制,用來完成運行時做用域、生存期等方面的處理。緩存
代碼分爲三種類型:閉包
這是一個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]]
知識點就是這些,如今咱們來一段代碼,把過程給串聯起來。在這以前,咱們要知道另外一個知識,就是執行上下文的代碼被分紅兩個基本的階段來處理
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加入到做用域鏈的頂端,這樣在標識符查找時,須要查找更長的做用域鏈。
閉包
理解了上面的概念,閉包就很容易理解了。閉包是代碼塊和建立該代碼塊的上下文中數據的結合。