用bind方法保持this上下文

學到bind方法這塊兒有些地方不太明白,自己就查了些資料,結合自己的理解寫了這篇文章以備後面回顧用。。。其實應該還是搬磚爲主吧。

什麼是this對象

先來說說什麼是this對象吧,每個函數在調用的時候都會自動獲取兩個特殊變量:this和arguments對象。this值具體是指哪個對象是和該函數的執行環境相關的。如果是作爲對象的方法,那麼this就是對象實例本身;如果是一個全局函數,那麼this就是window對象。用一句話來概括,this就是調用這個方法的對象。

保持this上下文

有時候,我們需要保持this的上下文,也就是在一個執行環境中想要訪問到另一個執行環境的this值。在什麼時候需要這麼做呢?比如說將一個對象的方法賦值給了一個全局變量,然後在全局變量中調用這個方法,那麼this值就不再是原來的對象而是window對象了,然而可能我們仍需要在全局環境中按照對象的方法來調用。又比如說一個方法中包含了閉包,閉包是無法訪問到其外部函數的this對象的,因爲this對象是在調用方法的時候自動生成,內部函數在搜索這兩個變量的時候只會搜索到其自身的活動對象,而不會沿着作用域鏈往外搜索,所以閉包訪問不到外部函數的this值。如果要想訪問,就應該想辦法把this值傳遞下去。 
通常可以通過這樣的方式保持this上下文:在外部函數中將this緩存到一個變量中,通常變量名稱使用self, _this 或者 context,那麼閉包就可以通過這個可訪問的變量來獲取外部函數的this值,this上下文得以保持。比如下面的代碼:

var myObj = {

    specialFunction: function () {},
    getAsyncData: function (cb) {
        cb();
    },

    render: function () {
        var that = this;
        this.getAsyncData(function () {
            that.specialFunction();
        });
    }
};
myObj.render();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

這裏有一個對象myObj,它有一個render實例方法,在這個方法內部又調用了它的另一個實例方法getAsyncData,而這個方法有一個新的函數作爲參數,這個函數相當於是一個閉包,是不能獲取到外部函數中的this值的,爲了在這個閉包中也能訪問實例方法,需要獲取到外部環境的this值,這裏把this(this爲調用render方法的對象,即實例對象myObj)緩存到了變量that中。

此外還可通過bind方法,這就是本文所要講述的重點。

bind方法

bind方法生成了一個新的函數,稱爲綁定函數,傳入bind方法的第一個參數作爲這個綁定函數的this對象,傳入bind的第二個參數連同後面調用綁定函數時傳入的參數按照先後順序(傳入bind的在前)構成綁定函數的參數。 
現在我們把上面的例子修改一下:

render: function () {
    this.getAsyncData(function () {

        this.specialFunction();

    }.bind(this));

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

.bind()創建了一個函數,當這個函數在被調用的時候,它的 this 關鍵詞會被設置成被傳入的值(這裏指調用bind()時傳入的參數) 
再看一個bind的使用例子:

var foo = {
    x: 3
} 
var bar = function(){
    console.log(this.x);
} 
bar(); 
// undefined

var boundFunc = bar.bind(foo);

boundFunc(); 
// 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

將bar方法和foo對象綁定後,bar中的this對象被替換爲了foo,並生成了一個新的函數boundFunc,因此在全局環境中調用boundFunc時,也可以訪問到foo對象的屬性。 
還可以瞭解一下Function.prototype.bind()內部是什麼樣的:

Function.prototype.bind = function (scope) {
    var fn = this;//this是調用bind方法的對象(別的方法對象)
    return function () {
        return fn.apply(scope);//把fn環境中的this替換爲scope
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可看出,bind方法返回了一個新的函數,這個方法返回了原方法(調用bind的方法)通過apply修改作用域(傳入的參數scope)後的執行結果。如果調用這個新函數則會立即執行fn.apply(scope),並返回執行後的結果。

fn.bind()
  • 1
  • 1

與call、apply的區別

call、apply是修改函數的作用域,並且立即執行,而bind是返回了一個新的函數,不是立即執行,即call and apply call a function while bind creates a function。bind在回調函數中常用到。

參考資料: 
理解 JavaScript 中的 Function.prototype.bind 
js中bind、call、apply函數的用法 
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章