JavaScript 中的 `call`、`apply` 和 `bind`

242 阅读4分钟

理解 JavaScript 中的 callapplybind

在 JavaScript 中,callapplybind 是所有函数对象都具备的方法,它们允许你控制函数的执行上下文(this)。理解这些方法对于掌握高级 JavaScript 技巧和有效管理 this 非常重要。本文将详细介绍这三个方法的区别和使用场景,帮助你深入理解它们的工作原理。


call 方法

定义

call 方法允许你用指定的 this 上下文和逐个传递的参数来调用一个函数。

语法

func.call(thisArg, arg1, arg2, ...);
  • thisArg:调用函数时用作 this 的值。
  • arg1, arg2, ...:传递给函数的参数。

示例

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Alice' };

// 使用 call 以 'person' 作为 `this` 调用 greet 函数
greet.call(person, 'Hello', '!');

解释

在这个示例中,greet.call(person, 'Hello', '!')person 作为 this,并将 'Hello''!' 作为参数传递给 greet 函数。输出将是:

Hello, Alice!

apply 方法

定义

apply 方法与 call 类似,但它接受一个参数数组而不是逐个参数。

语法

func.apply(thisArg, [arg1, arg2, ...]);
  • thisArg:调用函数时用作 this 的值。
  • [arg1, arg2, ...]:要传递给函数的参数数组或类数组对象。

示例

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Bob' };

// 使用 apply 以 'person' 作为 `this` 调用 greet 函数,并传递一个参数数组
greet.apply(person, ['Hi', '.']);

解释

在这个示例中,greet.apply(person, ['Hi', '.'])person 作为 this,并将数组 ['Hi', '.'] 作为参数传递给 greet 函数。输出将是:

Hi, Bob.

bind 方法

定义

bind 方法创建一个新的函数,这个函数在调用时会将 this 绑定到指定的值,并预先设置部分参数。

语法

const boundFunc = func.bind(thisArg, arg1, arg2, ...);
  • thisArg:新的函数在调用时用作 this 的值。
  • arg1, arg2, ...:预先设置的参数,将被传递给函数。

示例

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Charlie' };

// 使用 bind 绑定 'person' 作为 `this` 并预设参数
const boundGreet = greet.bind(person, 'Greetings');
boundGreet('!');

解释

在这个示例中,greet.bind(person, 'Greetings') 创建了一个新的函数 boundGreet,它在调用时会将 this 绑定到 person,并将 'Greetings' 作为预设参数。调用 boundGreet('!') 将输出:

Greetings, Charlie!

总结

  • callapply 用于立即调用函数,并允许你指定 this 和参数。不同之处在于,call 逐个传递参数,而 apply 通过数组传递参数。
  • bind 用于创建一个新的函数,这个函数具有指定的 this 值和预设的参数,可以在之后的调用中使用。

在 Vue 3 中,没有 this 的概念,主要是通过 setup 函数和 Composition API 来管理组件的状态和行为。尽管如此,callapplybind 这三个方法仍然在 Vue 3 中有用,特别是当你需要处理普通 JavaScript 函数或库函数时。以下是它们在 Vue 3 中的一些应用场景:

1. 处理普通 JavaScript 函数

即使在 Vue 3 中,你可能仍然会使用普通的 JavaScript 函数。这些函数可能会使用 callapplybind 来处理 this 上下文。比如,你可能会使用第三方库或自定义的函数,这些函数需要 this 上下文来运行。

function showMessage(message) {
  console.log(this.prefix + message);
}

const obj = { prefix: 'Hello ' };

const boundShowMessage = showMessage.bind(obj);
boundShowMessage('World'); // 输出 "Hello World"

2. 处理类和对象方法

在 Vue 3 中,你可能会使用类或对象方法来处理复杂的逻辑。callapplybind 在这种情况下仍然很有用,特别是当你需要动态设置 this 上下文时。

class Logger {
  constructor(prefix) {
    this.prefix = prefix;
  }

  log(message) {
    console.log(this.prefix + message);
  }
}

const logger = new Logger('Info: ');

const logFn = logger.log;
logFn.call(logger, 'This is a message.'); // 输出 "Info: This is a message."

3. 函数式编程

在 Vue 3 中使用函数式编程风格时,callapplybind 可以用来操作函数及其上下文。例如,在创建高阶函数时,它们可以帮助你控制函数的执行上下文。

const numbers = [1, 2, 3];

const sum = function() {
  return Array.prototype.reduce.call(arguments, (acc, curr) => acc + curr, 0);
};

console.log(sum(1, 2, 3)); // 输出 6

4. 在 Composition API 中使用

即使在 Vue 3 的 Composition API 中,你也可能会遇到需要处理函数上下文的情况。例如,在创建自定义的 hooks 或处理某些外部库时,bind 可以用于确保正确的上下文。

import { ref } from 'vue';

function useLogger() {
  const log = ref(null);

  const setLog = function() {
    log.value = this.prefix + ' Log Message';
  };

  return {
    log,
    setLog: setLog.bind({ prefix: 'Prefix: ' })
  };
}

const { log, setLog } = useLogger();
setLog();
console.log(log.value); // 输出 "Prefix: Log Message"

总结

尽管 Vue 3 中的组件方法和 setup 函数不使用 thiscallapplybind 在处理普通 JavaScript 函数、类和对象方法、函数式编程以及外部库时仍然有用。它们帮助你更灵活地管理函数的 this 上下文,并处理复杂的函数调用场景。

Function.prototype.call() - MDN
Function.prototype.apply() - MDN
Function.prototype.bind() - MDN

OBKoro1 - js 面试官想了解你有多理解 call,apply,bind?
Micherwa - 「干货」细说 call、apply 以及 bind 的区别和用法
公子 - 如何理解,javascript bind
Schaos - 一次搞懂前端面試最愛問的 apply、bind、call