【轉】【總結】函數上下文、this、調用棧、做用域鏈和活動對象等

函數上下文

關於函數內參數

http://www.cnblogs.com/ssh-007/p/5064699.htmljavascript

執行上下文

較詳細:http://www.shsxt.com/it/java/537.htmlhtml

https://github.com/cbbfcd/all-of-javascript/blob/master/deep-into-javascript/this.mdown前端

var name = 'The Window';
var obj = {
   name: 'My obj',
   getName: function() {
        return this.name;
    }
};

// 猜想下面的輸出和背後的邏輯(非嚴格模式下)
object.getName();                       //'My obj'
(object.getName)();                     //'My obj'
(object.getName = object.getName)();    //'The Window'

在函數被調用的時候java

1.會建立一個 執行環境及相應的做用域鏈git

2.使用arguments以及其餘命名參數的值來 初始化函數的活動對象 (activation object,簡稱AO)。github

3.在做用域上,函數會 逐層複製自身調用點的函數屬性,完成做用域鏈的構建,直到全局執行環境。閉包

例如:app

function compare(value1, value2) {
    return value1 - value2;
}

var result = compare(5, 10);

圖片: http://shsxt.shsxt.com/2017/1010/20171010091240828.pngssh

  1. 在這段代碼中,result經過var進行了變量聲明提高,compare經過function函數聲明提高,在代碼執行以前咱們的全局變量對象中就會有這兩個屬性。函數

  2. 每一個執行環境都會有一個變量對象,包含存在的全部變量的對象。全局環境的變量對象始終存在,而像compare函數這樣的局部環境的變量對象,則只在函數執行的過程當中存在。當建立compare()函數時,會建立一個預先包含全局變量對象的做用域鏈,這個做用域鏈保存在內部的[[Scope]]屬性中。

  3. 在調用compare函數時,會爲它建立一個執行環境,而後複製函數的[[scope]]屬性中的對象構建起執行環境的做用域鏈。此後,又有一個活動對象(變量對象)被建立並被推入執行環境做用域鏈的前端。此時做用域鏈包含兩個變量對象:本地活動對象和全局變量對象。顯然,做用域鏈本質上是一個指向變量對象的指針列表,它只引用但不包含實際的變量對象。

  4. 當訪問函數的變量時,就會從做用域鏈中搜索。當函數執行完畢後,局部活動對象就會被銷燬,內存中僅保存全局做用域。

閉包的狀況

可是,閉包的狀況有所不一樣,在一個函數內部定義的函數會將外部函數的活動對象添加到它的做用域鏈中去。

function create(property) {
  return function(object1, object2) {
      console.log(object1[property], object2[property]);
  };
}

var compare = create('name');
var result = compare({name: 'Nicholas'}, {name: 'Greg'}); // Nicholas Greg

// 刪除對匿名函數的引用,以便釋放內存
compare = null;

解析:

  1. 在匿名函數從create()中被返回後,它的做用域鏈被初始化爲包含create()函數的活動對象和全局變量對象。

  2. 這樣,該匿名函數就能夠訪問create中定義的全部遍歷,更爲重要的是當create()函數執行完畢後,其做用域鏈被銷燬,可是活動對象不會銷燬,由於依然被匿名函數引用。

  3. 當匿名函數別compare()被銷燬後,create()的活動對象纔會被銷燬。

閉包與this

咱們知道 this對象是基於函數的執行環境綁定的 ,在全局的時候,this等於window,而當函數做爲某個對象的方法調用時,this等於那個對象。

不過,匿名函數的執行環境具備全局性,所以this經常指向window。

var name = 'The Window';
var obj = {
   name: 'My obj',
   getName: function() {
       return function() {
            return this.name;
       };
   }
};

obj.getName()(); // 'The Window'

前面說過,函數在被調用時會自動取得兩個特殊變量: this和arguments,內部函數在搜索這兩個變量時,只會搜索到其活動對象,因此永遠不會訪問到外部函數的這兩個變量。若是咱們想知足需求,能夠固定this對象並改名便可。

var name = 'The Window';
var obj = {
   name: 'My obj',
   getName: function() {
       // 固定this對象,造成閉包,防止跟特殊的this重名
       var that = this;
       return function() {
          return that.name;
     };
  }
};

obj.getName()(); // 'My obj'

改變this指向還可使用 apply、call 函數

this的綁定

上面對this的說明能夠說是很是的淺薄了,如今咱們詳細的整理下this關鍵字.

this是函數做用域的特殊關鍵字,進入函數執行環境時會被自動定義,實現原理至關於 /自動傳遞調用點的對象 :

var obj = {
   name: 'Nicholas',
   speak() {
        return this.name;
   },
   anotherSpeak(context) {
       console.log(context.name, context === this);
   }
};

obj.name;    //'Nicholas'
obj.speak();    // 'Nicholas'
obj.anotherSpeak(obj);    // 'Nicholas' true

能夠看到,咱們在anotherSpeak()中傳遞的context就是obj,也就是函數調用時,執行環境的this值。引擎的這種實現簡化了咱們的工做,自動傳遞調用點的環境對象做爲this對象

咱們要注意的是this只跟調用點有關,而跟聲明點無關。這裏你須要知道 調用棧,也就是使咱們到達當前執行位置而被調用的全部方法的棧,即全部嵌套的函數棧。

this的四種綁定。

看文章吧。。

相關文章
相關標籤/搜索