首先框仔,什么是軟綁定舀武?
?所謂軟綁定,是和硬綁定相對應(yīng)的一個詞离斩,在詳細(xì)解釋軟綁定之前银舱,我們先來看看硬綁定。
在JavaScript中跛梗,this的綁定是動態(tài)的寻馏,在函數(shù)被調(diào)用的時候綁定,它指向什么完全取決于函數(shù)在哪里調(diào)用核偿,情況比較復(fù)雜诚欠,光是綁定規(guī)則就有默認(rèn)綁定、隱式綁定漾岳、顯式綁定轰绵、new綁定等,而硬綁定是顯式綁定中的一種尼荆,通常情況下是通過調(diào)用函數(shù)的 apply() 左腔、 call() 或者ES5里提供的 bind() 方法來實現(xiàn)硬綁定的。
硬綁定有什么問題捅儒,為什么需要軟綁定 液样?
上述三個方法好是好振亮,可以按照自己的想法將函數(shù)的this強(qiáng)制綁定到指定的對象上(除了使用new綁定可以改變硬綁定外),但是硬綁定存在一個問題蓄愁,就是會降低函數(shù)的靈活性双炕,并且在硬綁定之后無法再使用隱式綁定或者顯式綁定來修改this的指向。 在這種情況下撮抓,被稱為軟綁定的實現(xiàn)就出現(xiàn)了,也就是說摇锋,通過軟綁定丹拯,我們希望this在默認(rèn)情況下不再指向全局對象(非嚴(yán)格模式)或 undefined (嚴(yán)格模式),而是指向兩者之外的一個對象(這點和硬綁定的效果相同)荸恕,但是同時又保留了隱式綁定和顯式綁定在之后可以修改this指向的能力乖酬。
軟綁定的具體實現(xiàn)
?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;
};
}
我們先來看一下效果,之后再討論它的實現(xiàn)融求。
學(xué)習(xí)群64弍46衣3凌9咬像,資料群69似64陸0吧3
?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"
/*回調(diào)函數(shù)相當(dāng)于一個隱式的傳參陷舅,如果沒有軟綁定的話倒彰,這里將會應(yīng)用默認(rèn)綁定將this綁定到全局環(huán)? 境上,但有軟綁定莱睁,這里this還是指向obj1*/
可以看到軟綁定生效了待讳。下面我們來具體看一下 softBind() 的實現(xiàn)。
在第一行仰剿,先通過判斷创淡,如果函數(shù)的原型上沒有 softBind() 這個方法,則添加它南吮,然后通過 Array.prototype.slice.call(arguments,1) 獲取傳入的外部參數(shù)琳彩,這里這樣做其實為了函數(shù)柯里化,也就是說旨袒,允許在軟綁定的時候汁针,事先設(shè)置好一些參數(shù),在調(diào)用函數(shù)的時候再傳入另一些參數(shù)(關(guān)于函數(shù)柯里化大家可以去網(wǎng)上搜一下詳細(xì)的講解)最后返回一個 bound 函數(shù)形成一個閉包砚尽,這時候施无,在函數(shù)調(diào)用 softBind() 之后,得到的就是 bound 函數(shù)必孤,例如上面的 var fooOBJ=foo.softBind(obj1) 猾骡。 在 bound 函數(shù)中瑞躺,首先會判斷調(diào)用軟綁定之后的函數(shù)(如fooOBJ)的調(diào)用位置,或者說它的this的指向兴想,如果 !this (this指向undefined)或者 this===(window||global) (this指向全局對象)幢哨,那么就將函數(shù)的this綁定到傳入 softBind 中的參數(shù)obj上。如果此時this不指向undefind或者全局對象嫂便,那么就將this綁定到現(xiàn)在正在指向的函數(shù)(即隱式綁定或顯式綁定)捞镰。
?fn.apply 的第二個參數(shù)則是運行 foo 所需要的參數(shù),由上面的args(外部參數(shù))和內(nèi)部的arguments(內(nèi)部參數(shù))連接成毙替,也就是上面說的柯里化岸售。 其實在第一遍看這個函數(shù)時,也有點迷厂画,有一些疑問凸丸,比如 var fn=this 這句,在 foo 通過 foo.softBind() 調(diào)用 softBind 的時候袱院,fn到底指向誰呢屎慢?是指向foo還是指向softBind?我們可以寫個demo測試忽洛,然后可以很清晰地看出fn指向什么:
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的綁定規(guī)則的, softBind 函數(shù)定義在Function的原型 Function.prototype 中脐瑰,但是JavaScript中函數(shù)永遠(yuǎn)不會“屬于”某個對象(不像其他語言如java中類里面定義的方法那樣)妖枚,只是對象內(nèi)部引用了這個函數(shù),所以在通過下面兩種方式調(diào)用時苍在,fn(或者說this)分別隱式綁定到了 foo 和 Function.prototype 绝页,所以分別輸出3和4。后面的 fn.apply() 也就相當(dāng)于 foo.apply() 寂恬。