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.
apply()
方法會呼叫一個以 this 的代表值和一個陣列形式的值組(或是一個 array-like object)為參數的函式。
備註:這個函式的語法和 call()
幾乎一樣,最大的不同是 call()
接受一連串的參數,而 apply()
接受一組陣列形式的參數。
語法
fun.apply(thisArg, [argsArray])
參數
回傳值
傳入 this
值及一組參數後得到的結果。
描述
在呼叫一個現存的函式時,你可以傳入不同的 this
物件值。this 參考到現在的物件,也就是正在執行的物件。apply 讓你可以只寫一次方法後,讓其他物件也繼承到這個方法,而不用一再重寫。
apply
與 call()
非常相似,不同的是支援的傳入參數類型。使用陣列形式的參數,而不是命名過的接收參數。使用 apply
時,你可以選擇使用陣列實字:fun.apply(this, ['eat', 'bananas']);
或是 Array
物件:fun.apply(this, new Array('eat', 'bananas'))
。
除此之外,你也可以使用 arguments
代表 argsArray
參數。arguments 是在函式裡的區域變數,可用來存取所有沒有特別被所呼叫物件指定的傳入參數。因此,使用 apply 時你不需要知道所呼叫函式的指定參數。使用 arguments
把所有參數傳入呼叫的方法裡,而被呼叫的方法會接手處理這些參數。
從 ECMAScript 5th 版本後,也可以使用陣列形式的物件,在實踐上這代表他會擁有 length
以及整數範圍 (0...length-1)
的屬性。舉例來說,你可以使用 NodeList
或是一個像這樣的自定義屬性:{ 'length': 2, '0': 'eat', '1': 'bananas' }
。
備註:一般瀏覽器,包括 Chrome 14 及 Internet Explorer 9,仍不支援陣列形式的物件,所以會對此丟出一個錯誤。
範例
使用 apply
與建構子鏈結
你可以使用 apply
鏈結 constructors 一個物件,與 Java 相似,如下範例中我們可以建立一個全域的 Function
方法叫 construct
,使你可以使用類陣列的物件與建構子去替代參數列表。
Function.prototype.construct = function (aArgs) {
var oNew = Object.create(this.prototype);
this.apply(oNew, aArgs);
return oNew;
};
備註:如上範例的 Object.create()
方法是屬於比較新的寫法。如需使用閉包的替代方法,請參考以下的範例:
Function.prototype.construct = function (aArgs) {
var fConstructor = this,
fNewConstr = function () {
fConstructor.apply(this, aArgs);
};
fNewConstr.prototype = fConstructor.prototype;
return new fNewConstr();
};
使用範例:
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);
console.log(myInstance.property1); // logs 'Hello world!'
console.log(myInstance instanceof MyConstructor); // logs 'true'
console.log(myInstance.constructor); // logs 'MyConstructor'
備註:This non-native Function.construct
method will not work with some native constructors (like Date
, for example). In these cases you have to use the Function.prototype.bind
method (for example, imagine having 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
於內建的函數
apply 可以巧妙的在某些任務中使用內建函數,否則可能會循環遍歷整個陣列來寫入。如下範例,我們使用 Math.max/Math.min
來找出陣列中最大/最小的值。
// 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];
}
}
But beware: in using apply
this way, you run the risk of exceeding the JavaScript engine's argument length limit. The consequences of applying a function with too many arguments (think more than tens of thousands of arguments) vary across engines (JavaScriptCore has hard-coded argument limit of 65536), because the limit (indeed even the nature of any excessively-large-stack behavior) is unspecified. Some engines will throw an exception. More perniciously, others will arbitrarily limit the number of arguments actually passed to the applied function. (To illustrate this latter case: if such an engine had a limit of four arguments [actual limits are of course significantly higher], it would be as if the arguments 5, 6, 2, 3
had been passed to apply
in the examples above, rather than the full array.) If your value array might grow into the tens of thousands, use a hybrid strategy: apply your function to chunks of the array at a time:
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 built-in 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).
規範
Specification |
---|
ECMAScript Language Specification # sec-function.prototype.apply |
瀏覽器相容性
BCD tables only load in the browser