【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;
 }
复制代码

对于代码有疑问的小伙伴能够留言或者看注释!!!

相关文章
相关标签/搜索