自定义实现call apply bind

1,017 阅读2分钟

call方法作用

调用一个对象的一个方法,以另一个对象替换当前对象。也就是说改变方法中this指向,第一个参数代表this指向的对象,后面紧跟的参数为函数原有的形参

call方法的使用

  const A = {
    name: '我是A对象的name',
    getName: function () {
      console.log(this.name);
    },
  };
  const B = {
    name: '我是B对象的name'
  }
  A.getName() // //结果 我是A对象的name
  A.getName.call(B) //结果 我是B对象的name
  1. 使用call改变了A对象中getName方法的this指向,使其指向了B对象
  2. fn.call(obj)可以看成obj.fn(...args),这样fn中的this就指向了obj
  3. 我们可以使用call方法,借助其他对象中存在的方法实现自己的功能,例如es5将伪数组转为真正的数组,new 关键字的实现也借助了call方法等

call方法的参数

  const A = {
    name: '我是A对象的name',
    getName: function (a,b,c) {
      console.log(this.name,a+b+c);
    },
  };

  const B = {
  name: '我是B对象的name'
  }
  A.getName(2,3,4) // //结果 我是A对象的name 9
  A.getName.call(B,1,2,3) //结果 我是B对象的name 6
  1. call方法第一个参数为this指向的对象,后面的参数为原方法中的参数,依次传递
  2. fn.apply(obj)可以看成obj.fn([...args]),这样fn中的this就指向了obj
  3. call的语法:函数名.call(obj,参数1,参数2,参数3...)

自定义call方法

  const A = {
    name: '我是A对象的name',
    getName: function (a,b,c) {
      console.log(this.name,a+b+c);
    },
  };
  const B = {
    name: '我是B对象的name'
  }
  //call前面是fucntion函数,所以该方法需要添加到Function构造函数的原型对象上
  Function.prototype.myCall = function (context,...args) {
    if(typeof this !== 'function'){
      throw new Error(`${this} is not function`)
    }
    if(!context instanceof Object){
      throw new Error(`${context} is not Object`)
    }
    //将函数绑定到对象上作为对象上的方法
    context.fn = this
    //执行对象中的方法
    const result =  context.fn(...args)
    //删除被绑定的方法
    delete context['fn']
    return result
 }
  A.getName.myCall(B,1,2,3) //我是B对象的name 6
  A.getName.call(B,1,2,3)   //我是B对象的name 6

apply与call对比

  1. 功能相同,都是改变this指向

  2. 语法不同 apply的语法:函数名.call(obj,[参数1,参数2,参数3...])

apply方法的使用

  const A = {
    name: '我是A对象的name',
    getName: function (a,b,c) {
      console.log(this.name,a+b+c);
    },
  };
  const B = {
    name: '我是B对象的name'
  }
  A.getName.apply(B,[1,2,3]) //输出 我是B对象的name 6

自定义apply方法

  const A = {
    name: '我是A对象的name',
    getName: function (a,b,c) {
      console.log(this.name,a+b+c);
    },
  };
  const B = {
    name: '我是B对象的name'
  }
  Function.prototype.myApply = function (context,args) {
    if(typeof this !== 'function'){
      throw new Error(`${this}is not function` )
    }
    if(!context instanceof Object){
      throw new Error(`${context} is not Object`)
    }
    if(args && !Array.isArray(args)){
      throw new Error(`${args} is not Array`)
    }
    context.fn = this
    const result = context.fn(...args)
    return result
  }
  A.getName.myApply(B,[1,2,3]) //输出 我是B对象的name 6

bind与apply和call对比

  1. 功能相同,都是改变this指向
  2. 返回值不同,bind会返回一个函数,我们需要调用这个返回值
  3. 入参与call相同

bind方法的使用

  const A = {
    name: '我是A对象的name',
    getName: function (a,b,c) {
      console.log(this.name,a+b+c);
    },
  };

  const B = {
    name: '我是B对象的name'
  }
  A.getName.bind(B)(1,2,3) //输出 我是B对象的name 6

自定义bind方法

  const A = {
    name: '我是A对象的name',
    getName: function (a,b,c) {
      console.log(this.name,a+b+c);
    },
  };

  const B = {
    name: '我是B对象的name'
  }
  Function.prototype.myBind = function (context) {
    const self = this;
    if (typeof self !== "function") {
      throw new Error(`${self}is not function`);
    }
    if (!context instanceof Object) {
      throw new Error(`${context} is not Object`);
    }
    //bin方法返回一个函数
    return function (...args) {
      context.fn = self
      const result = context.fn(...args);
      return result;
    };
  };
  A.getName.myBind(B)(1, 2, 3); //输出 我是B对象的name 6