Function.prototype.apply()

apply() 方法是 Function 实例的方法,它使用给定的 this 值和以数组形式提供的 arguments 调用此函数(或类似数组的对象)。

试试看

语法

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),区别在于 args 在前者使用 apply() 时应为类似数组的对象,而在后者使用展开语法时应为 可迭代 对象。

警告: 不要使用 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. simple loop based algorithm
max = -Infinity;
min = +Infinity;

for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] > max) {
    max = numbers[i];
  }
  if (numbers[i] < min) {
    min = numbers[i];
  }
}

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

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

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

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 语言规范
# sec-function.prototype.apply

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅