用call或bind實現bind()

  • 2019 年 10 月 3 日
  • 筆記

一、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轉換後的數組。