手写 bind call apply,一些自己的总结和理解

48 阅读2分钟

call

以 call 为例子,我们来分析下实现一个 call 函数我们需要思考些什么?

首先,我们来分析下实现一个 call,我们需要实现那些功能:

  1. 这个方法是挂在函数原型上的;
  2. 这个函数接受几个参数,第一个参数是需要指向的this,后面的参数是传入函数的参数;
  3. 这个方法会被立即执行,并且被执行的函数有返回值,最后也会返回这个值;
  4. 当第一个参数为null、undefined、空时,默认为window;
  5. 当 this 指向为非引用对象的时候,this 会指向对应类型的实例;

call 实现

Function.prototype._call=function(){
    //将类数组转换为数组
    //1.Array.from
    //2.const args=Array.prototype.slice.apply(arguments)
    //3.for of ... for in ...
    const args=Array.from(arguments)
    //取出需要改变的this指向
    let target=args.shift() || window
    //处理this指向为非引用类型时
    if(typeof target!=='object'){
        target=new target.constructor()
    }
    //添加唯一键值
    const fn=Symbol()
    //给指定目标添加当前要执行的函数即this,这也是最重要的一步
    target[fn]=this
    const result = args ? target[fn](...args) : target[fn]()
    //删除添加的属性,避免污染
    delete target[fn]
    //返回函数执行的结果
    return result
}

apply实现

Function.prototype._apply = function (target) {
  //空值处理
  target = target || window;
  if (typeof target !== "object") {
    target = new target.constructor();
  }
  //创建一个唯一的键值,避免冲突
  const fn = Symbol();
  //将函数挂载在指定目标的 fn 上
  target[fn] = this;
  //提取函数参数
  const args = arguments[1];
  const result = args ? target[fn](...args) : target[fn]();
  //删除引用
  delete target[fn];
  //返回函数执行的结果
  return result;
};

bind 手写

Function.prototype._bind=function(){
  const args=Array.from(arguments);
  const fn = Symbol()
  let target=args.shift() || window;
  if(typeof target !== 'object'){
    target=new target.constructor()
  }
  target[fn]=this
  // const _this=this
  return function (...args){
    target[fn](...args)
    // _this.apply(target,[...args])
    delete target[fn]
  }
}

总结

其实只要能实现call、bind、apply其中的任何一个,另外的2个都很好实现,只是在处理函数参数的时候有些许不同,还有要注意的点就是call、apply改变this指向会立即执行,而bind则需要再次执行,因为它返回的是一个函数。重点在于理解this的指向问题,能搞懂不同情况的this指向,那么实现这些就不是问题。