实现 apply/call/bind

apply

1. 语法

1func.apply(thisArg, [argsArray]);

参数

thisArg

在 func 函数运行时使用的 this 值。

argsArray

一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。

返回值

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

2. 流程图

apply

3. 编写代码

1Function.prototype.apply = function (thisArg, argsArray) {
2  if (typeof this !== "function") {
3    throw new TypeError(
4      "Function.prototype.apply was called on which is not a function"
5    );
6  }
7
8  if (thisArg === undefined || thisArg === null) {
9    thisArg = window;
10  } else {
11    thisArg = Object(thisArg);
12  }
13
14  // 将 func 放入 thisArg 内,这样在调用 thisArg[func] 时 this 自然就指向了 thisArg
15  const func = Symbol("func");
16  thisArg[func] = this;
17
18  let result;
19
20  if (argsArray && typeof argsArray === "object" && "length" in argsArray) {
21    // 此处使用 Array.from 包裹让其支持形如 { length: 1, 0: 1 } 这样的类数组对象,直接对 argsArray 展开将会执行出错
22    result = thisArg[func](...Array.from(argsArray));
23  } else if (argsArray === undefined || argsArray === null) {
24    result = thisArg[func]();
25  } else {
26    throw new TypeError("CreateListFromArrayLike called on non-object");
27  }
28
29  delete thisArg[func];
30
31  return result;
32};

call

1. 语法

1func.call(thisArg, arg1, arg2, ...)

参数

thisArg

在 func 函数运行时使用的 this 值。

arg1, arg2, ...

指定的参数列表。

返回值

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

2. 流程图

call

3. 编写代码

1Function.prototype.call = function (thisArg, ...argsArray) {
2  if (typeof this !== "function") {
3    throw new TypeError(
4      "Function.prototype.call was called on which is not a function"
5    );
6  }
7
8  if (thisArg === undefined || thisArg === null) {
9    thisArg = window;
10  } else {
11    thisArg = Object(thisArg);
12  }
13
14  // 将 func 放入 thisArg 内,这样在调用 thisArg[func] 时 this 自然就指向了 thisArg
15  const func = Symbol("func");
16  thisArg[func] = this;
17
18  let result;
19
20  if (argsArray.length) {
21    result = thisArg[func](...argsArray);
22  } else {
23    result = thisArg[func]();
24  }
25
26  delete thisArg[func];
27
28  return result;
29};

bind

1. 语法

1func.bind(thisArg[, arg1[, arg2[, ...]]])

参数

thisArg

调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用 new 运算符构造绑定函数,则忽略该值。

arg1, arg2, ...

当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

返回值

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

2. 流程图

bind

3. 编写代码

1Function.prototype.bind = function (thisArg, ...argsArray) {
2  if (typeof this !== "function") {
3    throw new TypeError(
4      "Function.prototype.bind was called on which is not a function"
5    );
6  }
7
8  if (thisArg === undefined || thisArg === null) {
9    thisArg = window;
10  } else {
11    thisArg = Object(thisArg);
12  }
13
14  const func = this;
15
16  const bound = function (...boundArgsArray) {
17    let isNew = false;
18
19    // 如果 func 不是构造器,直接使用 instanceof 将出错,所以需要用 try...catch 包裹
20    try {
21      isNew = this instanceof func;
22    } catch (error) {}
23
24    return func.apply(isNew ? this : thisArg, argsArray.concat(boundArgsArray));
25  };
26
27  const Empty = function () {};
28  Empty.prototype = this.prototype;
29  bound.prototype = new Empty();
30
31  return bound;
32};

参考资料

  1. Function.prototype.apply()
  2. Function.prototype.call()
  3. Function.prototype.bind()