手动实现bind方法

Javascript天下第一 !

接上一篇文章,我们来一起实现一下bind方法
老规矩,来分析下bind的作用以及参数,返回值等信息
bind方法的全称是Function.prototype.bind(),官方是这么介绍的

bind()方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项

官方的描述直接介绍了bind做了什么

  1. 返回了一个新函数
  2. 将this(传入的参数)关键字绑定到该函数
  3. 参数合并,将bind函数的参数与原来的函数参数合并作为参数传给创建的新的函数
  4. 返回该函数

但是bind与call和apply又有区别,一个函数被call的时候,会直接去调用,但是bind是会返回一个函数,当这个函数执行的时候,bind()的第一个参数将作为它运行时的this。

好了,bind就是做了这个事情,所以现在要一步步去实现它

  • 首先去创建一个函数,这个函数需要有原函数的prototype的值
Function.prototype._bind = function(){
  //首先去缓存参数列表,避免直接更改参数列表
  let _arguments = arguments;
  //类数组转换成数组
   _arguments = Array.prototype.slice.call(_arguments);
  // 创建一个函数,
  let fn = function(){}
  //这个函数需要有所有的prototype的值
  fn.prototype = this.prototype;
}
  • 紧接着将this指向这个函数
Function.prototype._bind = function(){
  //首先去缓存参数列表,避免直接更改参数列表
  let _arguments = arguments;
  //类数组转换成数组
   _arguments = Array.prototype.slice.call(_arguments);
  //拿到当前方法
  let _this = this;
  //拿到指定的this值,shift操作会改变原数组
  let target = Array.prototype.shift.call(_arguments);
  // 创建一个函数,
  let fn = function(){
     _this.apply(target);
  }
  //这个函数需要有所有的prototype的值
  fn.prototype = this.prototype;
}
  • 然后合并参数(bind函数的参数与被bind函数的参数)
Function.prototype._bind = function(){
  //首先去缓存参数列表,避免直接更改参数列表
  let _arguments = arguments;
  //类数组转换成数组
   _arguments = Array.prototype.slice.call(_arguments);
  //拿到当前方法
  let _this = this;
  //拿到指定的this值,shift操作会改变原数组
  let target = Array.prototype.shift.call(_arguments);
  // 创建一个函数,
  let fn = function(){
     _this.apply(target,_arguments.concat(Array.prototype.slice.call(arguments)));
  }
  //这个函数需要有所有的prototype的值
  fn.prototype = this.prototype;
}
  • 最后将这个函数返回
Function.prototype._bind = function(){
  //首先去缓存参数列表,避免直接更改参数列表
  let _arguments = arguments;
  //类数组转换成数组
   _arguments = Array.prototype.slice.call(_arguments);
  //拿到当前方法
  let _this = this;
  //拿到指定的this值,shift操作会改变原数组
  let target = Array.prototype.shift.call(_arguments);
  // 创建一个函数,并执行合并之后的参数
  let fn = function(){
     _this.apply(target,_arguments.concat(Array.prototype.slice.call(arguments)));
  }
  //添加原函数所有的prototype的值
  fn.prototype = this.prototype;
  //最后返回这个方法
  return fn
}

如此一个简单的bind方法就已经实现了。但是这里有个问题,就是当bind之后的函数,如果被当作构造函数去new的话,new出来的实例的指针指的是原来的bind方法,而不是bind之后的方法,这里有点绕,我先用变量来代表一下:

  • A函数 ----代表原函数,也就是需要被bind的函数 A.bind(ctx);
  • B函数 ----代表bind之后的函数,也就是B = A.bind(ctx);
  • C函数 ----代表new一个B之后得到的实例函数 C = new B();
    也就是说,如果用上面写的自定义bind方法,new出来的C的构造函数是A函数,但是我们需要的并不是A函数,我们要的是B函数
 function A(name){
     this.name = name
 }
  var obj = {}
  var B = A._bind(obj)
  var C = new B();
  //按照上一章节的原型规则中的第四条
  //所有的引用类型(数组,对象,函数),__ proto__属性指向它的构造函数的prototype属性值,这里C的构造函数是B
  //但是这里却是false,
  console.log(C.__proto__ === B.prototype)  //false      
  //相反
  console.log(C.__proto__ === A.prototype)  //true

所以此时的C函数的构造函数是A并不是B,所以我们需要重新写一下上面的代码,需要判断返回的函数(B函数)是不是被当作构造函数使用的,怎么判断呢,其实很简单,判断B函数的this instanceof B是否是true,如果是的话,说明此时函数被当作构造函数来使用了,这个时候,apply里面的target不能使用外部传入的指针了,应该直接使用this,所以,整理一下,代码应该是这样:

Function.prototype._bind = function(){
  //首先去缓存参数列表,避免直接更改参数列表
  let _arguments = arguments;
  //类数组转换成数组
   _arguments = Array.prototype.slice.call(_arguments);
  //拿到当前方法
  let _this = this;
  //拿到指定的this值,shift操作会改变原数组
  let target = Array.prototype.shift.call(_arguments);
  // 创建一个函数,并执行合并之后的参数
  let fn = function(){
    let ctx = this instanceof fn ? this : target
     _this.apply(ctx,_arguments.concat(Array.prototype.slice.call(arguments)));
  }
  //添加原函数所有的prototype的值
  fn.prototype = this.prototype;
  //最后返回这个方法
  return fn
}

好了,这下是可以满足要求了,但是还是有一个小问题,由于对象属于引用类型,直接进行赋值语句操作的话,后面改动一个,另一个也会改变,因为指针指向的是同一个内存地址,所以上面代码中的最后一部分应该做一个clone

Function.prototype._bind = function(){
  //首先去缓存参数列表,避免直接更改参数列表
  let _arguments = arguments;
  //类数组转换成数组
   _arguments = Array.prototype.slice.call(_arguments);
  //拿到当前方法
  let _this = this;
  //拿到指定的this值,shift操作会改变原数组
  let target = Array.prototype.shift.call(_arguments);
  // 创建一个函数,并执行合并之后的参数
  let fn = function(){
    let ctx = this instanceof fn ? this : target
     _this.apply(ctx,_arguments.concat(Array.prototype.slice.call(arguments)));
  }
  //添加原函数所有的prototype的值
  fn.prototype = Object.create(this.prototype);
  //最后返回这个方法
  return fn
}

好了,大功告成!

你可能感兴趣的:(手动实现bind方法)