Function.prototype.apply()
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
尝试一下
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
语法
apply(thisArg)
apply(thisArg, argsArray)
参数
返回值
使用指定的 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
,例如:
function wrapper() {
return anotherFn.apply(null, arguments);
}
function wrapper(...args) {
return anotherFn(...args);
}
一般而言,fn.apply(null, args)
等同于使用参数展开语法的 fn(...args)
,只是在前者的情况下,args
期望是类数组对象,而在后者的情况下,args
期望是可迭代对象。
警告:
不要使用 apply()
进行构造函数链式调用(例如,实现继承)。这会将构造函数作为普通函数调用,这意味着 new.target
是 undefined
,从而类会抛出错误,因为它们不能在没有 new
的情况下调用。请改用 Reflect.construct()
或 extends
。
示例
用 apply() 将数组各项添加到另一个数组
你可以使用 Array.prototype.push()
方法将元素追加到数组中。因为 push()
接受可变数量的参数,所以你也可以一次性添加多个元素。但是,如果你将一个数组传递给 push()
,它实际上会将该数组作为单个元素添加,而不是逐个添加元素,导致最终得到一个数组内嵌的数组。另一方面,Array.prototype.concat()
在这种情况下具有期望的行为,但它不会将元素追加到已有数组中,而是创建并返回一个新数组。
在这种情况下,你可以使用 apply
隐式地将一个数组作为一系列参数展开。
const array = ["a", "b"];
const elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
使用展开语法可以达到相同的效果。
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()
来找出数组中的最大值和最小值。
// 数组中的最小/最大值
const numbers = [5, 6, 2, 3, 7];
// 用 apply 调用 Math.min/Math.max
let max = Math.max.apply(null, numbers);
// 这等价于 Math.max(numbers[0], …) 或 Math.max(5, 6, …)
let min = Math.min.apply(null, numbers);
// 与基于简单循环的算法相比
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
,而不是完整的数组。
如果你的值数组可能会增长到数万个,可以使用混合策略:将数组的片段分批通过 apply
调用函数:
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]);
规范
Specification |
---|
ECMAScript® 2025 Language Specification # sec-function.prototype.apply |
浏览器兼容性
BCD tables only load in the browser