JavaScript
实现 call、apply、bind

call

使用指定的 this 值和参数调用函数的结果。

func.call(thisArg, arg1, arg2, ...)
Function.prototype.call = function (thisArg, ...argsArray) {
  // 首先判断调用对象是否为函数,不是的话抛出一个 TypeError 异常
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.call was called on which is not a function");
  }
 
  // 判断 thisArg 是否为 null 或者 undefined
  // 是的话将 window 作为 this 值,否则将 thisArg 转换为对象并作为 this 值
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }
 
  // 将 func 放入 thisArg 内,这样在调用 thisArg[func] 时 this 自然就指向了 thisArg
  const func = Symbol("func");
  thisArg[func] = this;
 
  let result;
 
  // 判断有没有参数,有的话将参数传入 func
  if (argsArray.length) {
    result = thisArg[func](...argsArray);
  } else {
    result = thisArg[func]();
  }
 
  delete thisArg[func];
 
  // 返回执行结果
  return result;
};

apply

使用指定的 this 值和参数调用函数的结果。

func.apply(thisArg, [argsArray]);
Function.prototype.apply = function (thisArg, argsArray) {
  // 首先判断调用对象是否为函数,不是的话抛出一个 TypeError 异常
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.apply was called on which is not a function");
  }
 
  // 判断 thisArg 是否为 null 或者 undefined
  // 是的话将 window 作为 this 值,否则将 thisArg 转换为对象并作为 this 值
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }
 
  // 将 func 放入 thisArg 内,这样在调用 thisArg[func] 时 this 自然就指向了 thisArg
  const func = Symbol("func");
  thisArg[func] = this;
 
  let result;
 
  // 判断 argsArray 是否为数组或者类数组对象
  if (argsArray && typeof argsArray === "object" && "length" in argsArray) {
    // 将 argsArray 展开传入 func
    // 此处使用 Array.from 包裹让其支持形如 { length: 1, 0: 1 } 这样的类数组对象,直接对 argsArray 展开将会执行出错
    result = thisArg[func](...Array.from(argsArray));
  } else if (argsArray === undefined || argsArray === null) {
    // 没有传入参数
    result = thisArg[func]();
  } else {
    // 传入的参数不是数组或者类数组对象,抛出一个 TypeError 异常
    throw new TypeError("CreateListFromArrayLike called on non-object");
  }
 
  delete thisArg[func];
 
  // 返回执行结果
  return result;
};

bind

返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

func.bind(thisArg[, arg1[, arg2[, ...]]])
Function.prototype.bind = function (thisArg, ...argsArray) {
  // 首先判断调用对象是否为函数,不是的话抛出一个 TypeError 异常
  if (typeof this !== "function") {
    throw new TypeError("Function.prototype.bind was called on which is not a function");
  }
 
  // 判断 thisArg 是否为 null 或者 undefined
  // 是的话将 window 作为 this 值,否则将 thisArg 转换为对象并作为 this 值
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }
 
  const func = this;
 
  // 创建一个函数
  const bound = function (...boundArgsArray) {
    // 判断是否是使用 new 运算符构造的函数
    let isNew = false;
 
    // 如果 func 不是构造器,直接使用 instanceof 将出错,所以需要用 try...catch 包裹
    try {
      isNew = this instanceof func;
    } catch (error) {}
 
    // 如果是使用 new 运算符构造的函数,实例作为 this 值,否则使用 thisArg 作为 this 值
    // 函数内部使用指定的 this 值和参数列表调用 apply 并返回结果
    return func.apply(isNew ? this : thisArg, argsArray.concat(boundArgsArray));
  };
 
  // 使用空函数连接函数与 func 之间的 prototype
  const Empty = function () {};
  Empty.prototype = this.prototype;
  bound.prototype = new Empty();
 
  // 返回函数
  return bound;
};