1. 程式人生 > >用call或bind實現bind()

用call或bind實現bind()

一、bind方法

讓我們看一下MDN上對bind方法的解釋

    bind()方法建立一個新的函式,在bind()被呼叫時,這個新函式的this被bind的第一個引數指定,其餘的引數將作為新函式的引數供呼叫時使用。

 也就是說,bind()方法會:

  • 建立一個新的函式(這也是它和call、apply不同的點)
  • 建立的函式接收bind的第二個及以後的引數作為自己的引數

 那bind建立的這個新函式還有其他什麼特性嗎?

    呼叫繫結函式時作為this引數傳遞給目標函式的值。 如果使用new運算子構造繫結函式,則忽略該值。

 舉個下面的例子:由bind建立的新函式bindFoo作為建構函式時,其建立的例項newBindFoo並不指向bindFoo繫結的obj,而是指向bindFoo。

var obj={
    name:"Melody"
}
var name="huyang"
function foo(tel){
    console.log(this.name)
    console.log(tel)
}
var bindFoo=foo.bind(obj,"110")

bindFoo()
//Melody
//110

var newbindFoo=new bindFoo();
//undefinde
//110

二、現在可以嘗試用call實現bind啦

先實現前兩個特性,用call模擬bind繫結this,並且對arguments進行分割處理實現其餘引數傳遞

Function.prototype.bind2 = function (context) {
    	var self = this;
    	var args=Array.prototype.slice.call(arguments,1)//模擬bind時的傳參
    	return function () {
    		var bindArgs=Array.prototype.slice(arguments)//模擬執行bind的返回函式時的傳參
        	self.apply(context,args.concat(bindArgs));//修改返回函式的this指向為context,並將bind時和執行bind的返回函式傳入的引數concat後繫結給返回函式。
    	}
}

修改返回函式的作用域鏈,使其指向繫結函式,這樣返回函式生成的例項就可以繼承繫結函式的原型啦。

Function.prototype.bind2 = function (context) {
    	var self = this;
    	var args=Array.prototype.slice.call(arguments,1)//模擬bind時的傳參
    	var foo=function() {
    		var bindArgs=Array.prototype.slice(arguments)//模擬執行bind的返回函式時的傳參
        	self.apply(this instanceof self ? this : context, args.concat(bindArgs));
                // 由於下方修改返回函式的prototype為繫結函式的prototype,當返回函式作為建構函式使用時,例項this instanceof self必定為真(instanceof判斷的底層原理實際上就是根據原型鏈判斷的)
                // 當作為普通函式時,this 指向 window,self 指向繫結函式,此時結果為 false,當結果為 false 的時候,this 指向繫結的 context
    
    	}
        foo.prototype=this.prototype
        return foo
}

需要注意的點:

    arguments只是具有length屬性且可以通過index讀取的類陣列物件,並沒有slice等陣列方法,要想對arguments使用陣列方法必須得將arguments轉換為真正的陣列。故,使用Array.prototype.slice.call(arguments),對Array原型鏈中的slice方法呼叫call(或apply),傳入arguments作為其上下文,然後返回arguments轉換後的陣列。

  

&n