關於JavaScript中this的軟綁定

首先,什麼是軟綁定?

所謂軟綁定,是和硬綁定相對應的一個詞,在詳細解釋軟綁定以前,咱們先來看看硬綁定。在JavaScript中,this的綁定是動態的,在函數被調用的時候綁定,它指向什麼徹底取決於函數在哪裏調用,狀況比較複雜,光是綁定規則就有默認綁定、隱式綁定、顯式綁定、new綁定等,而硬綁定是顯式綁定中的一種,一般狀況下是經過調用函數的apply()call()或者ES5裏提供的bind()方法來實現硬綁定的。java

硬綁定有什麼問題,爲何須要軟綁定

上述三個方法好是好,能夠按照本身的想法將函數的this強制綁定到指定的對象上(除了使用new綁定能夠改變硬綁定外),可是硬綁定存在一個問題,就是會下降函數的靈活性,而且在硬綁定以後沒法再使用隱式綁定或者顯式綁定來修改this的指向。bash

在這種狀況下,被稱爲軟綁定的實現就出現了,也就是說,經過軟綁定,咱們但願this在默認狀況下再也不指向全局對象(非嚴格模式)或undefined(嚴格模式),而是指向二者以外的一個對象(這點和硬綁定的效果相同),可是同時又保留了隱式綁定和顯式綁定在以後能夠修改this指向的能力。閉包

軟綁定的具體實現

在這裏,我用的是《你不知道的JavaScript 上》中的軟綁定的代碼實現:app

if(!Function.prototype.softBind){
    Function.prototype.softBind=function(obj){
        var fn=this;
        var args=Array.prototype.slice.call(arguments,1);
        var bound=function(){
            return fn.apply(
                (!this||this===(window||global))?obj:this,
                args.concat.apply(args,arguments)
            );
        };
        bound.prototype=Object.create(fn.prototype);
        return bound;
    };
}
複製代碼

咱們先來看一下效果,以後再討論它的實現。函數

function foo(){
    console.log("name: "+this.name);
}

var obj1={name:"obj1"},
    obj2={name:"obj2"},
    obj3={name:"obj3"};

var fooOBJ=foo.softBind(obj1);
fooOBJ();//"name: obj1" 在這裏軟綁定生效了,成功修改了this的指向,將this綁定到了obj1上

obj2.foo=foo.softBind(obj1);
obj2.foo();//"name: obj2" 在這裏軟綁定的this指向成功被隱式綁定修改了,綁定到了obj2上

fooOBJ.call(obj3);//"name: obj3" 在這裏軟綁定的this指向成功被硬綁定修改了,綁定到了obj3上

setTimeout(obj2.foo,1000);//"name: obj1"
/*回調函數至關於一個隱式的傳參,若是沒有軟綁定的話,這裏將會應用默認綁定將this綁定到全局環
境上,但有軟綁定,這裏this仍是指向obj1*/
複製代碼

能夠看到軟綁定生效了。下面咱們來具體看一下softBind()的實現。測試

在第一行,先經過判斷,若是函數的原型上沒有softBind()這個方法,則添加它,而後經過Array.prototype.slice.call(arguments,1)獲取傳入的外部參數,這裏這樣作其實爲了函數柯里化,也就是說,容許在軟綁定的時候,事先設置好一些參數,在調用函數的時候再傳入另外一些參數(關於函數柯里化你們能夠去網上搜一下詳細的講解)最後返回一個bound函數造成一個閉包,這時候,在函數調用softBind()以後,獲得的就是bound函數,例如上面的var fooOBJ=foo.softBind(obj1)ui

bound函數中,首先會判斷調用軟綁定以後的函數(如fooOBJ)的調用位置,或者說它的this的指向,若是!this(this指向undefined)或者this===(window||global)(this指向全局對象),那麼就將函數的this綁定到傳入softBind中的參數obj上。若是此時this不指向undefind或者全局對象,那麼就將this綁定到如今正在指向的函數(即隱式綁定或顯式綁定)。fn.apply的第二個參數則是運行foo所須要的參數,由上面的args(外部參數)和內部的arguments(內部參數)鏈接成,也就是上面說的柯里化。this

其實在第一遍看這個函數時,也有點迷,有一些疑問,好比var fn=this這句,在foo經過foo.softBind()調用softBind的時候,fn到底指向誰呢?是指向foo仍是指向softBind?咱們能夠寫個demo測試,而後能夠很清晰地看出fn指向什麼:spa

var a=2;
function foo(){
}
foo.a=3;
Function.prototype.softBind=function(){
    var fn=this;
    return function(){
        console.log(fn.a);
    }
};
Function.prototype.a=4;
Function.prototype.softBind.a=5;

foo.softBind()();//3
Function.prototype.softBind()();//4
複製代碼

能夠看出,fn(或者說this)的指向仍是遵循this的綁定規則的,softBind函數定義在Function的原型Function.prototype中,可是JavaScript中函數永遠不會「屬於」某個對象(不像其餘語言如java中類裏面定義的方法那樣),只是對象內部引用了這個函數,因此在經過下面兩種方式調用時,fn(或者說this)分別隱式綁定到了fooFunction.prototype,因此分別輸出3和4。後面的fn.apply()也就至關於foo.apply()prototype

第一次在掘金寫博客,之後多寫點吧,也算是對知識點的一個總結,同時也但願能幫到你們。emmm 就這樣。

相關文章
相關標籤/搜索