概述
apply()
方法可以在使用一个指定的 this
值和一个参数数组(或类数组对象)的前提下调用某个函数或方法。
语法
fun.apply(thisArg[, argsArray])
参数
-
在
fun
函数运行时指定的this
值。
需要注意的是,指定的this
值并不一定是该函数执行时真正的this
值,如果这个函数处于 非严格模式下,则指定为null
或undefined
时会自动指向
全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this
会指向该原始值的自动包装对象。 -
一个数组或者类数组对象,其中的数组元素将作为单独的参数传给
fun
函数。如果该参数的值为null
或undefined
,则表示不需要传入任何参数。
thisArg
argsArray
查看bug 562448了解这个变化的详情.
描述
在调用一个存在的函数时,你可以为其指定一个 this
对象。 this
指当前对象,也就是正在调用这个函数的对象。 使用 apply
, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。
apply
与 call()
非常相似,不同在于所提供的参数类型。apply
使用参数数组而不是一组参数列表(原文:a named set of parameters)。使用 apply
, 可以使用数组字面量(array literal)形式,例如,fun.apply(this, ['eat', 'bananas'])
,,或者数组对象形式, 例如, fun.apply(this, new Array('eat', 'bananas'))
。
你也可以使用 arguments
对象作为 argsArray
参数。 arguments
是一个函数的局部变量。 It can be used for all unspecified arguments of the called object. Thus, you do not have to know the arguments of the called object when you use the apply
method. You can use arguments
to pass all the arguments to the called object. The called object is then responsible for handling the arguments.
从 ECMAScript 第5版开始,可以使用任何种类的类数组对象,就是说只要有一个 length
属性和[0...length)
范围的整数属性。例如现在可以使用 NodeList
或一个自己定义的类似 {'length': 2, '0': 'eat', '1': 'bananas'}
形式的对象。
示例
Using apply
to chain constructors
You can use apply
to chain constructors for an object, similar to Java. In the following example we will create a global Function
method called construct
, which will make you able to use an array-like object with a constructor instead of an arguments list.
Function.prototype.construct = function (aArgs) {
var fConstructor = this, fNewConstr = function () { fConstructor.apply(this, aArgs); };
fNewConstr.prototype = fConstructor.prototype;
return new fNewConstr();
};
Example usage:
function MyConstructor () {
for (var nProp = 0; nProp < arguments.length; nProp++) {
this["property" + nProp] = arguments[nProp];
}
}
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
alert(myInstance.property1); // alerts "Hello world!"
alert(myInstance instanceof MyConstructor); // alerts "true"
alert(myInstance.constructor); // alerts "MyConstructor"
Function.construct
method will not work with some native constructors (like
Date
, for example). In these cases you have to use the
Function.bind
method (for example, imagine to have an array like the following, to be used with
Date
constructor:
[2012, 11, 4]
; in this case you have to write something like:
new (Function.prototype.bind.apply(Date, [null].concat([2012, 11, 4])))()
– anyhow this is not the best way to do things and probably should not be used in any production environment).
apply
and built-in functions
Clever usage of apply
allows you to use built-ins functions for some tasks that otherwise probably would have been written by looping over the array values. As an example here we are going to use Math.max/Math.min to find out the maximum/minimum value in an array.
/* min/max number in an array */
var numbers = [5, 6, 2, 3, 7];
/* using Math.min/Math.max apply */
var max = Math.max.apply(null, numbers); /* This about equal to Math.max(numbers[0], ...) or Math.max(5, 6, ..) */
var min = Math.min.apply(null, numbers);
/* vs. simple loop based algorithm */
max = -Infinity, min = +Infinity;
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] > max)
max = numbers[i];
if (numbers[i] < min)
min = numbers[i];
}
但是当心: 如果用上面的方式调用
当你对一个方法传入非常多的参数 (比如超过1W多个参数) 时, 就非常有可能会导致越界问题, 这个临界值是根据不同的 JavaScript 引擎而定的 (JavaScript 核心中已经做了硬编码 argument limit of 65536). 有些引擎会抛出异常. 更糟糕的是其他引擎会直接限制传入到方法的参数个数,导致参数丢失. (举个例子: 如果某个引擎限制了方法参数最多为4个 [实际真正的参数个数限制当然要高得多了, 这里只是打个比方], 上面的代码中, 真正通过 apply
, 你很可能会遇到方法参数个数越界的问题. apply
传到目标方法中的参数为 5, 6, 2, 3, 而不是完整的 numbers 数组.) 如果你的参数数组可能非常大, 那么推荐使用下面这种策略来处理: 将参数数组切块后循环传入目标方法:
function minOfArray(arr) {
var min = Infinity;
var QUANTUM = 32768;
for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
min = Math.min(submin, min);
}
return min;
}
var min = minOfArray([5, 6, 2, 3, 7]);
Using apply in "monkey-patching"
Apply can be the best way to monkey-patch a builtin function of Firefox, or JS libraries. Given someobject.foo function, you can modify the function in a somewhat hacky way, like so:
var originalfoo = someobject.foo;
someobject.foo = function() {
//Do stuff before calling function
console.log(arguments);
//Call the function as it would have been called normally:
originalfoo.apply(this,arguments);
//Run stuff after, here.
}
This method is especially handy where you want to debug events, or interface with something that has no API like the various .on([event]... events, such as those usable on the Devtools Inspector).