Function.prototype.apply()

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

apply() 方法用于调用一个函数,该函数有一个给定的 this 值,以及以数组(或 类数组对象)形式提供的参数。

试一试

const numbers = [5, 6, 2, 3, 7];

const max = Math.max.apply(null, numbers);

console.log(max);
// Expected output: 7

const min = Math.min.apply(null, numbers);

console.log(min);
// Expected output: 2

语法

js
apply(thisArg)
apply(thisArg, argsArray)

参数

thisArg

为调用 func 提供的 this 值。如果函数不在 严格模式下,nullundefined 将被替换为全局对象,原始值将被转换为对象。

argsArray 可选

一个类数组对象,指定了调用 func 时应使用的参数,如果不想为函数提供任何参数,则可以为 nullundefined

返回值

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

描述

注意: 这个函数几乎与 call() 相同,只是 call() 将函数参数作为单独的列表传入,而 apply() 则将它们组合在一个对象中(通常是数组)——例如,func.call(this, "eat", "bananas") 对比 func.apply(this, ["eat", "bananas"])

通常,在调用函数时,函数内部的 this 值是函数被访问的对象。通过 apply(),您可以在调用现有函数时将任意值分配给 this,而无需先将函数作为属性附加到对象上。这允许您将一个对象的方法用作通用实用函数。

您还可以使用任何类数组对象作为第二个参数。实际上,这意味着它需要有一个 length 属性,以及一个整数(“索引”)属性,其范围为 (0..length - 1)。例如,您可以使用 NodeList,或者一个自定义对象,如 { 'length': 2, '0': 'eat', '1': 'bananas' }。您也可以使用 arguments,例如

js
function wrapper() {
  return anotherFn.apply(null, arguments);
}

使用 剩余参数和参数 展开语法,可以改写为

js
function wrapper(...args) {
  return anotherFn(...args);
}

一般来说,fn.apply(null, args) 等同于带有参数展开语法的 fn(...args),只是 apply() 期望 args 是一个类数组对象,而展开语法期望的是一个 可迭代 对象。

警告: 请勿使用 apply() 来链接构造函数(例如,来实现继承)。这会将构造函数作为普通函数调用,这意味着 new.targetundefined,并且类会因为无法在没有 new 的情况下调用而抛出错误。请改用 Reflect.construct()extends

示例

使用 apply() 将一个数组追加到另一个数组

您可以使用 Array.prototype.push() 将一个元素追加到数组中。因为 push() 接受可变数量的参数,所以您也可以一次性追加多个元素。但是,如果您将一个数组传递给 push(),它实际上会将该数组作为一个单独的元素添加,而不是单独添加元素,最终结果是数组中包含一个数组。另一方面,Array.prototype.concat() 在这种情况下可以实现所需行为,但它不会追加到现有数组——它会创建并返回一个新数组。

在这种情况下,您可以使用 apply 来隐式地将一个数组“展开”为一系列参数。

js
const array = ["a", "b"];
const elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]

使用展开语法可以达到相同的效果。

js
const array = ["a", "b"];
const elements = [0, 1, 2];
array.push(...elements);
console.info(array); // ["a", "b", 0, 1, 2]

使用 apply() 和内置函数

巧妙使用 apply() 可以让您使用内置函数来完成一些原本可能需要手动循环遍历集合(或使用展开语法)的任务。

例如,我们可以使用 Math.max()Math.min() 来找出数组中的最大值和最小值。

js
// min/max number in an array
const numbers = [5, 6, 2, 3, 7];

// using Math.min/Math.max apply
let max = Math.max.apply(null, numbers);
// This about equal to Math.max(numbers[0], …)
// or Math.max(5, 6, …)

let min = Math.min.apply(null, numbers);

// vs. loop based algorithm
max = -Infinity;
min = Infinity;

for (const n of numbers) {
  if (n > max) {
    max = n;
  }
  if (n < min) {
    min = n;
  }
}

但请注意:使用 apply()(或展开语法)并传入任意长度的参数列表,您可能会面临超出 JavaScript 引擎参数长度限制的风险。

用过多的参数(即,超过数万个参数)调用函数的后果是未定义的,并且因引擎而异。(JavaScriptCore 引擎有一个硬编码的 参数限制,为 65536)。大多数引擎会抛出异常;但没有规范性说明可以阻止其他行为,例如任意限制实际传递给被应用函数的参数数量。为了说明后一种情况:如果某个引擎的限制是四个参数(实际限制当然远高于此),那么在上面的示例中,就好像只将参数 5, 6, 2, 3 传递给了 apply,而不是整个数组。

如果您的值数组可能会增长到数万个,请使用混合策略:一次将函数应用于数组的各个块

js
function minOfArray(arr) {
  let min = Infinity;
  const QUANTUM = 32768;

  for (let i = 0; i < arr.length; i += QUANTUM) {
    const subMin = Math.min.apply(
      null,
      arr.slice(i, Math.min(i + QUANTUM, arr.length)),
    );
    min = Math.min(subMin, min);
  }

  return min;
}

const min = minOfArray([5, 6, 2, 3, 7]);

规范

规范
ECMAScript® 2026 语言规范
# sec-function.prototype.apply

浏览器兼容性

另见