关于call、apply和bind,请看这篇

Alanxz 2020-03-04

call

func.call(context, arg1, arg2, ...)

参数说明:
  context :在 func函数运行时指定的this值,当第一个参数为null、undefined的时候,默认指向window。
  arg1, arg2:…指定的参数列表。
返回值:
  使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回undefined。


apply(与call相似,只是参数不同而已)

func.apply(context, arr)

参数说明:
  context :在 func函数运行时指定的this值,当第一个参数为null、undefined的时候,默认指向window。
  arr 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。


bind

func.bind(context, arg1, arg2, ...)

参数说明:
  context :在 func函数运行时指定的this值,当第一个参数为null、undefined的时候,默认指向window。
  arg1, arg2:…指定的参数列表。
返回值:
  返回一个原函数的拷贝,并拥有指定的this值和初始参数。

例如:

  var name = ‘小王‘, age = 17;
    var obj = {
        name: ‘小张‘,
        age: this.age,
        myFun: function(fm, t) {
            console.log(this.name + ‘ 年龄‘ + this.age, ‘ 来自‘ + fm + ‘去往‘ + t);
        }
    }
    var db = {
        name: ‘德玛‘,
        age: 99
    }
  // 以下将obj的myFun方法应用到db对象上
    obj.myFun.call(db,‘成都‘,‘上海‘);     // 德玛 年龄 99  来自 成都去往上海
    obj.myFun.apply(db,[‘成都‘,‘上海‘]);      // 德玛 年龄 99  来自 成都去往上海  
    obj.myFun.bind(db,‘成都‘,‘上海‘)();       // 德玛 年龄 99  来自 成都去往上海
    obj.myFun.bind(db,[‘成都‘,‘上海‘])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined

区别

1、都是用来改变函数的this对象的指向的,this指向他们的第一个参数。
2、call跟apply的用法几乎一样,唯一的不同就是传递的参数不同,call只能一个参数一个参数的传入。
apply则只支持传入一个数组,哪怕是一个参数也要是数组形式。最终调用函数时候这个数组会拆成一个个参数分别传入。
3、bind方法是直接改变这个函数的this指向并且返回一个新的函数,之后再次调用这个函数的时候this都是指向bind绑定的第一个参数。bind传参方式跟call方法一致。
4、如果第一个参数为null或undefined,则this值会自动指向全局对象(浏览器中就是window对象)。

call/apply/bind的核心理念:借用方法。借助已实现的方法,改变方法中数据的this指向,减少重复代码,节省内存。

手写call、apply和bind

// 通过隐式绑定实现
Function.prototype.call = function(context, ...args) {
  // 如果没有传或传的值为空对象 context指向window
  context = context || window;
  // 给context添加一个方法 指向this
  context.func = this;

  if (typeof context.func !== ‘function‘) {
    throw new TypeError(‘call must be called on a function‘);
  }
  // 执行func
  let res = context.func(...args);
  // 删除方法,否则context就无缘无故多了个func
  delete context.func;
  return res;
}
Function.prototype.apply = function(context, args) {
  context = context || window;
  context.func = this;

  if (typeof context.func !== ‘function‘) {
    throw new TypeError(‘apply must be called on a function‘);
  }

  let res = context.func(...args);
  delete context.func;
  return res;
}
Function.prototype.bind = function(context, ...bindArgs) {
  context = context || window;
  // func 为调用 bind 的原函数
  const func = this;

  if (typeof func !== ‘function‘) {
    throw new TypeError(‘Bind must be called on a function‘);
  }
  // bind 返回一个绑定 this 的函数
  return function(...callArgs) {
    let args = bindArgs.concat(callArgs);
    if (this instanceof func) {
      // 意味着是通过 new 调用的 而 new 的优先级高于 bind
      return new func(...args);
    }
    return func.call(context, ...args);
  }
}

相关推荐