JS中的new和做用域鏈

new的運行機制

當代碼new Animal("cat")執行時:html

  1. var obj=Object.create(Animal.prototype);前端

  2. 傳入cat參數,構造函數Animal執行。同時構造函數內部的this被指定爲obj。函數

  3. 若是構造函數返回了一個「對象」,那麼這個對象就是new出來的結果。若是構造函數沒有返回對象(即返回一個非對象值,例如數值,或者無返回值),那麼new出來的結果爲obj對象。通常狀況下構造函數不返回值,除非你想要覆蓋正常建立的對象(即obj)。this

例如:spa

function A(name){
  this.name=name;
  return 3;
}
var new1=new A("aa");
new1;//A {name: "aa"}

function B(name){
  this.name=name;
  return {};
}
var new2=new B("aa");
new2;//new2爲一個空對象。

做用域鏈

JS權威指南中有一句很精闢的描述: 「JavaScript中的函數運行在它們被定義的做用域裏,而不是它們被執行的做用域裏。」簡單來講,就是函數被調用時,它是運行在當時定義該函數時的環境中的。prototype

定義函數

定義函數a的時候,js解釋器會將函數a的做用域鏈(scope chain)設置爲定義a時所在的「環境」,併爲a添加scope屬性,a.scope=a的做用域鏈。若是a定義在全局環境,那麼scope chain中只有window對象。code

調用函數

函數被調用時,會建立一個活動對象(call object)(也就是一個對象), 而後把全部函數a的局部變量和函數定義添加爲該活動對象的屬性, 並將該活動對象添加到a的做用域鏈的最頂端,此時a的做用域鏈包含2個對象:a的活動對象和window對象。htm

案例解析

爲何調用func1(10)和func2(10)時,引用到了兩個不一樣的i?對象

function outerFn(i, j) {
    var x = i + j;
    return function innerFn(x) {
        return i + x;
    }
}
var func1 = outerFn(5, 6);
var func2 = outerFn(10, 20);
alert(func1(10)); //返回15
alert(func2(10)); //返回20

調用outerFn (5, 6)的時候定義了一個新的函數對象innerFn,而後該函數對象成爲了outerFn函數的活動對象的一個屬性。這時innerFn的做用域鏈是由outerFn的活動對象和全局對象組成的.。這個做用域鏈存儲在了innerFn函數的內部屬性[[scope]]中,而後返回了該函數,變量func1就指向了這個innerFn函數。blog

在func1被調用時,它自身的活動對象被建立,而後添加到了[[scope]]中存儲着的做用域鏈的最前方。這時的做用域鏈纔是func1函數執行時用到的做用域鏈。從這個做用域鏈中,能夠看到變量‘i’的值實際上就是在執行outerFn(5,6)時產生的活動對象的屬性i的值。下圖顯示了整個流程。

clipboard.png

下圖是func2執行時的狀況。由於在定義func1和func2時,函數outerFn中產生過兩個不一樣的活動對象,因此才致使調用func1(10)和func2(10)時,引用到了兩個不一樣的i。

clipboard.png

一個活動對象在函數執行的時候建立,同時被添加到該函數的做用域鏈的最前端。當函數執行完畢時,活動對象會被從該做用域鏈上刪除。可是該活動對象是否會被垃圾回收器銷燬,還要看其餘地方是否還有使用到該活動對象。

參考自:
new運算符
Javascript做用域原理
[譯]JavaScript:函數的做用域鏈

相關文章
相關標籤/搜索