理解 JavaScript 的 `call`、`apply` 和 `bind` 方法

130 阅读2分钟

在 JavaScript 中,callapplybind 是 Function.prototype 上的三个重要方法,用于改变函数执行时的 this 指向。这些方法在日常开发中非常有用,理解它们的工作原理并能够自己实现它们,对提升 JavaScript 编程水平大有裨益。本文将讲解如何自定义实现这些方法,并提供示例代码。

自定义实现 call 方法

call 方法用于调用一个函数,并显式地设置 this 的值,同时可以传递多个参数。

实现代码

Function.prototype.myCall = function (context, ...args) {
  // 如果 context 为 null 或 undefined,则默认为全局对象(浏览器环境下是 window)
  context = context || window;

  // 将当前函数设为 context 对象的一个属性
  context.__fn__ = this;

  // 使用 context 对象调用函数,传入参数
  const result = context.__fn__(...args);

  // 删除 context 对象上的临时属性
  delete context.__fn__;

  return result;
};

// 示例使用
function greet(greeting) {
  console.log(`${greeting}, ${this.name}!`);
}

const person = { name: 'John' };

greet.myCall(person, 'Hello'); // 输出: Hello, John!

代码解释

  1. 设置上下文对象:如果 contextnullundefined,则默认指向全局对象 window
  2. 临时属性:将当前函数(this)作为 context 对象的一个属性。
  3. 调用函数:使用 context 对象调用该函数,并传入参数。
  4. 删除临时属性:调用完成后,删除临时添加的属性。

自定义实现 apply 方法

apply 方法类似于 call,但参数是以数组形式传入的。

实现代码

Function.prototype.myApply = function (context, argsArray) {
  // 如果 context 为 null 或 undefined,则默认为全局对象(浏览器环境下是 window)
  context = context || window;

  // 将当前函数设为 context 对象的一个属性
  context.__fn__ = this;

  // 使用 context 对象调用函数,传入参数数组
  const result = context.__fn__(...argsArray);

  // 删除 context 对象上的临时属性
  delete context.__fn__;

  return result;
};

// 示例使用
function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

const person = { name: 'John' };

greet.myApply(person, ['Hello', '!']); // 输出: Hello, John!

代码解释

myCall 方法类似,只是将参数数组 argsArray 展开并传递给函数。

自定义实现 bind 方法

bind 方法创建一个新函数,在调用时 this 值为提供的参数,并且可以在绑定时预置一些参数。

实现代码

Function.prototype.myBind = function (context, ...boundArgs) {
  // 保存原函数引用
  const originalFunction = this;

  // 返回一个新函数
  return function (...args) {
    // 使用 apply 方法调用原函数,传入指定的上下文、绑定参数和调用时的参数
    return originalFunction.apply(context, [...boundArgs, ...args]);
  };
};

// 示例使用
function greet(greeting, punctuation, additionalText) {
  console.log(`${greeting}, ${this.name}${punctuation}${additionalText}`);
}

const person = { name: 'John' };

const boundFunction = greet.myBind(person, 'Hello', '!');
boundFunction('Additional text'); // 输出: Hello, John!Additional text

代码解释

  1. 保存原函数引用:将当前函数(this)保存到 originalFunction 变量。
  2. 返回新函数:返回一个新函数,在调用时使用 apply 方法将 contextboundArgsargs 合并传递给原函数。