源碼來襲:bind手寫實現


理解建議:如果對this指向規則不了解的話,建議先了解this指向規則,最好還能對call和apply的使用和內部原理也有所了解,不然直接研究bind還是會有些難度的。

 一、bind()的使用

//html
<button id="btn"></button>
//js
var list = {
    init:function(){
        this.ms = "duyi";
        this.dom = document.getElementById("btn");
        this.bindEvent();
    },
    bindEvent:function(){
        this.dom.onclick = this.showMessage.bind(this);
    },
    showMessage:function(){
        alert(this.ms);
    }
}
list.init();

在單對象編程中,有一種非常典型的bind()的應用,就以上面的示例來說,當出現給DOM綁定事件回調函數時,又還需要繼續保持函數的this指向原來的對象,就可以按照示例的這種方式來實現:this.dom.onclick = this.showMessage.bind(this);

我們知道call和apply可以改變函數執行的this指向,但是call和apply都是立即執行該函數,而bind是將this指向綁定到指定的對象上,並且返回函數並維持this指向這個對象。接下來再來看看bind的參數設置示例:

function show(x,y,z,w){
    console.log(this,x,y,z,w);
}
var DuyiO = {
    x : 20
}
var newShow = show.bind(DuyiO,"1","2",3);
newShow(4);//Object {x: 20} "1" "2" 3 4

bind的參數和call非常類似,唯一的區別就在於除了調用bind時傳入參數外,還可以在正式執行時傳入參數,兩次傳入參數以拼接的方式作為函數執行的實參。但是需要注意的是,第一個參數作為函數的this指向對象必須要在調用bind方法時傳入,如果調用bind方法不傳入任何參數,函數的this指向就會綁定到window上。比如下面這種情況:

var newShow = show.bind();
newShow(DuyiO,"1","2",3,4);//Window {…} Object {x: 20} "1" "2" 3

最后還有一個基本上不會被應用到的功能,就是返回的函數被new關鍵字用來創建一個新的對象,而構造函數還是原函數本身(第二個示例中的show)。這個功能在模仿bind源碼不能100%實現,但是也可以間接的實現其需要的功能。

 二、bind手寫實現

1.首先實現函數調用bind修改this指向即參數設置:

1 Function.prototype.MyBind = function(target){
2     var self = this;
3     var args = [].slice.call(arguments,1);
4     var f = function(){
5         return self.apply( target || window,args );
6     }
7     return f;
8 }

2.接着再來實現函數正式調用執行時傳入設置:

1 Function.prototype.MyBind = function(target){
2     var self = this;
3     var args = [].slice.call(arguments,1);
4     var f = function(){
5         var _arg = [].slice.call(arguments,0);
6         return self.apply( target || window,args.concat(_arg) );
7     }
8     return f;
9 }

3.最后實現當返回函數被new操作符引用作為構造函數依然指向原函數(模擬實現功能):

 1 Function.prototype.MyBind = function(target){
 2     var self = this;
 3     var args = [].slice.call(arguments,1);
 4     var temp = function(){};
 5     var f = function(){
 6         var _arg = [].slice.call(arguments,0);
 7         return self.apply(this instanceof temp ? this : ( target || window ),args.concat(_arg) );
 8     }
 9     temp.prototype = self.prototype;
10     f.prototype = new temp();
11     return f;
12 }

這個模擬實現主要有兩個關鍵點需要重點理解:

a.代碼第七行中的this instanceof temp ? this : ( target || window ):當返回函數f被new作為構造函數引用時,這時候this指向了函數執行時內部隱式添加在變量對象上的this(這里不清楚的話可以參考JavaScript中的this指向規則),當然普通調用執行就是指向self。

b.代碼第九行和第十行為什么需要改變f的原型,這就是我前面講的模擬實現方法構造,我們知道bind在JS內部實現的是其返回函數還是那個原來的函數,這里我們多加了一層f來實現的,所以在函數被當做構造函數的時候,將f的原型指向self也可以實現其功能,但是構造的實例對象是基於f實現的,最終構造原型鏈還是指向self原型,該有的方法屬性依然都會有。只是在原型鏈上多了f這個包裝層。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM