【js基礎修煉之路】- 手把手教你實現bind

手寫bind前咱們先回顧一下bind有哪些特性,以便更好的理解bind和實現bind。bash

bind的特性

var obj = {
    a: 100,
    say(one, two) {
        console.log(this.a, one, two);
    }
}
var obj2 = {
    a: 300
}
var res = obj.say.bind(obj2, 1, 2);
res();
//300 1 2
複製代碼

能夠看出:app

  • bind是函數的方法,只有函數能夠調用函數

  • bind的第一個參數是this指向,剩下的參數做爲調用者的參數post

  • bind方法返回的是一個函數,須要再次調用才能執行ui

function test(){
	this.a = 10,
	this.b = 20
};
var foo = {
	a:200
}
var res = test.bind(foo);
var res2 = new res();
console.log(res2);
//test {a: 10, b: 20}
複製代碼

從上面能夠看出,new以後this執行不跟隨bind的第一個參數了,知道new是怎麼實現的小夥伴必定知道爲何(不知道的能夠看一下這篇文章的末尾 juejin.im/post/5c4926… ) 此時的this指向了res2。 知道了bind的特性,下面咱們來實現一下bindthis

手把手教你實現bind

咱們知道bind返回的是一個函數,調用者也是一個函數,而且bind改變了this指向,並且bind還能夠傳參,下面咱們來實現一下這些功能:spa

Function.prototype.bind = function(oThis) {
 	// 判斷調用者是否是函數
 	if(typeof this != 'function'){
 		  throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
     }
     //保存this(this指向調用bind者)
     var fToBind = this;
     //獲取傳入bind函數的第二個及其後面的參數(除去this參數)
     var aArgs = Array.prototype.slice.call(arguments,1);
     var fBound = function(){
     	//this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用
     	//== false的時候說明當作了普通函數來調用,this爲bind的第一個參數
     	return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
     }
     // 返回新函數
     return fBound;
 }
複製代碼

上面的代碼實現了一個基本的bind,可是還有一些問題,例如上面只是綁定了this,可是原函數的原型新函數並無繼承,因此咱們須要再次繼承一下原型:prototype

Function.prototype.bind = function(oThis) {
 	// 判斷調用者是否是函數
 	if(typeof this != 'function'){
 		  throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
     }
     //保存this(this指向調用bind者)
     var fToBind = this;
     //獲取傳入bind函數的第二個及其後面的參數(除去this參數)
     var aArgs = Array.prototype.slice.call(arguments,1);
     var fBound = function(){
     	//this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用
     	//== false的時候說明當作了普通函數來調用,this爲bind的第一個參數
     	return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
     }
      //綁定原型
      fBound.prototype = this.prototype;
     // 返回新函數
     return fBound;
 }
複製代碼

本覺得大功告成,可是還有一個問題,看下面的例子:code

function bar() {}
 var bindFoo = bar.bind(null);
 bindFoo.prototype.value = 1;
 console.log(bar.prototype.value) // 1
複製代碼

我只改變了bindFoo的原型,bar的爲何也跟着變了,由於在寫bind的時候把bar的原型賦給了bindFoo,因此致使了這種狀況,下面咱們用一箇中轉的函數來解決這個問題:對象

Function.prototype.bind = function(oThis) {
 	// 判斷調用者是否是函數
 	if(typeof this != 'function'){
 		  throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
     }
     //保存this(this指向調用bind者)
     var fToBind = this;
     //獲取傳入bind函數的第二個及其後面的參數(除去this參數)
     var aArgs = Array.prototype.slice.call(arguments,1);
     var fNOP = function() {};
     var fBound = function(){
     	//this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用
     	//== false的時候說明當作了普通函數來調用,this爲bind的第一個參數
     	return  fToBind.apply(this instanceof fBound ? this : oThis,aArgs.concat(Array.prototype.slice.call(arguments)));
     }
  	 // 爲了讓 fBound 構造的實例可以繼承綁定函數的原型中的值
     if (this.prototype) {
         fNOP.prototype = this.prototype;
     }
     // 下行的代碼使fBound.prototype是fNOP的實例,所以
     // 返回的fBound若做爲new的構造函數,new生成的新對象做爲this傳入fBound,新對象的__proto__就是fNOP的實例
     fBound.prototype = new fNOP();
     // 返回新函數
     return fBound;
 }
複製代碼

對於代碼有疑問的小夥伴能夠留言或者看註釋!!!

相關文章
相關標籤/搜索