call,apply,bind的简单实现小记

543 阅读3分钟

call,apply,bind的简单实现

call, apply和bind是Function.prototype下的方法,都是可以用来改变函数运行时的上下文(this)。

call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象(非严格模式下,第一个参数为null或undefined,指向全局对象),第二个参数不一样;

call的实现

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数

注意:该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组

**语法:**function.call(thisArg, arg1, arg2, ...)

思路:

  • 接收参数changeThis, params
  • 把函数当前的上下文this,存放到changeThis的新增属性fn上
  • 然后传入参数,并执行fn,得到返回结果res
  • 将changeThis新增的fn属性删除
  • 返回执行结果res
// 简单的实现,changeThis未做深入检测
// es6
Function.prototype._call = function (changeThis,...args) {
  if (typeof this !== 'function') {
  	throw new TypeError('error!')
  }
  if (changeThis === null || changeThis === undefined) {
    changeThis = window;
  } 
  changeThis.fn = this; // 把当前的this存放到changeThis的属性上
  const res = changeThis.fn(...args); // 传入参数,执行函数
  delete changeThis.fn; // 删除添加的属性
  return res; // 返回执行的结果
}

// 不适用es6 ,借助eval()方法实现
Function.prototype.$call = function (changeThis) {
  if (typeof this !== 'function') {
    throw new TypeError('error!')
  }
  if (changeThis === null || changeThis === undefined) {
    changeThis = window;
  } 
  changeThis.fn = this; // 把当前的this存放到changeThis的属性上
  var args = [];
  for (var i = 1; i < arguments.length; i++) {
    args.push('arguments(' + i + ')'); // 得到参数数组
  }
  var res = eval('changeThis.fn(' + args + ')'); // 传入参数,执行函数
  delete changeThis.fn; // 删除添加的属性
  return res; // 返回执行的结果
}

apply的实现

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

**语法:**func.apply(thisArg, [argsArray])

思路:

  • 与call的思路差不多,只是参数处理略微不同
// es6 与 call的实现类似
Function.prototype._apply = function (changeThis, params) {
  if (typeof this !== 'function') {
    throw new TypeError('error!')
  }
  if (changeThis === null || changeThis === undefined) {
    changeThis = window;
  }
  changeThis.fn = this; // 把当前的this(即调用的对象)存放到changeThis的属性上
  const fnArg = params || [];
  const res = changeThis.fn(...fnArg); // 传入参数,执行函数
  delete changeThis.fn; // 删除添加的属性
  return res; // 返回执行的结果
}

bind的实现

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

**语法:**function.bind(thisArg[, arg1[, arg2[, ...]]])

思路:

  • 缓存当前的this
  • 定义一个新的函数
  • 新的函数内部实现改变函数运行时的上下文的代码,并执行函数,返回执行的结果
  • 新定义的函数,要继承当前上下文的原型链
  • 返回该新定义的函数
Function.prototype._bind = function (changeThis, ...args) {
  if (typeof this !== 'function') {
    throw new TypeError('error!')
  }
  if (changeThis === null || changeThis === undefined) {
    changeThis = window;
  }
  const that = this; // 缓存当前的this
  const newFn = function () { // 定义需要返回的函数
    // 返回函数调用,并修改this
    let newThis = this instanceof newFn ? this : that;
    // 借助call来改变运行时的上下文,若不需要,则使用下面注释部分的代码
    const res = newThis._call(newThis, ...args); 
    // changeThis.fn = newThis;
    // const res = changeThis.fn(newThis, ...arr);
    // delete changeThis.fn;
    return res;
  }
  if (this.prototype) {
    newFn.prototype = Object.create(this.prototype); // 返回的新函数,需要继承当前this的原型链
  }
  return newFn; // 返回新的函数
  // return 一个function
}