JavaScript 中每個函數(shù)內(nèi)都能訪問一個特別變量 arguments。這個變量維護著所有傳遞到這個函數(shù)中的參數(shù)列表。
注意: 由于
arguments已經(jīng)被定義為函數(shù)內(nèi)的一個變量。 因此通過var關(guān)鍵字定義arguments或者將arguments聲明為一個形式參數(shù), 都將導(dǎo)致原生的arguments不會被創(chuàng)建。
arguments 變量不是一個數(shù)組(Array)。
盡管在語法上它有數(shù)組相關(guān)的屬性 length,但它不從 Array.prototype 繼承,實際上它是一個對象(Object)。
因此,無法對 arguments 變量使用標準的數(shù)組方法,比如 push, pop 或者 slice。
雖然使用 for 循環(huán)遍歷也是可以的,但是為了更好的使用數(shù)組方法,最好把它轉(zhuǎn)化為一個真正的數(shù)組。
下面的代碼將會創(chuàng)建一個新的數(shù)組,包含所有 arguments 對象中的元素。
Array.prototype.slice.call(arguments);
這個轉(zhuǎn)化比較慢,在性能不好的代碼中不推薦這種做法。
下面是將參數(shù)從一個函數(shù)傳遞到另一個函數(shù)的推薦做法。
function foo() {
bar.apply(null, arguments);
}
function bar(a, b, c) {
// 干活
}
另一個技巧是同時使用 call 和 apply,創(chuàng)建一個快速的解綁定包裝器。
function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
// 創(chuàng)建一個解綁定的 "method"
// 輸入?yún)?shù)為: this, arg1, arg2...argN
Foo.method = function() {
// 結(jié)果: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);
};
譯者注:上面的 Foo.method 函數(shù)和下面代碼的效果是一樣的:
Foo.method = function() {
var args = Array.prototype.slice.call(arguments);
Foo.prototype.method.apply(args[0], args.slice(1));
};
arguments 對象為其內(nèi)部屬性以及函數(shù)形式參數(shù)創(chuàng)建 getter 和 setter 方法。
因此,改變形參的值會影響到 arguments 對象的值,反之亦然。
function foo(a, b, c) {
arguments[0] = 2;
a; // 2
b = 4;
arguments[1]; // 4
var d = c;
d = 9;
c; // 3
}
foo(1, 2, 3);
不管它是否有被使用,arguments 對象總會被創(chuàng)建,除了兩個特殊情況 - 作為局部變量聲明和作為形式參數(shù)。
arguments 的 getters 和 setters 方法總會被創(chuàng)建;因此使用 arguments 對性能不會有什么影響。
除非是需要對 arguments 對象的屬性進行多次訪問。
ES5 提示: 這些 getters 和 setters 在嚴格模式下(strict mode)不會被創(chuàng)建。
譯者注:在 MDC 中對 strict mode 模式下 arguments 的描述有助于我們的理解,請看下面代碼:
// 闡述在 ES5 的嚴格模式下 `arguments` 的特性
function f(a) {
"use strict";
a = 42;
return [a, arguments[0]];
}
var pair = f(17);
assert(pair[0] === 42);
assert(pair[1] === 17);
然而,的確有一種情況會顯著的影響現(xiàn)代 JavaScript 引擎的性能。這就是使用 arguments.callee。
function foo() {
arguments.callee; // do something with this function object
arguments.callee.caller; // and the calling function object
}
function bigLoop() {
for(var i = 0; i < 100000; i++) {
foo(); // Would normally be inlined...
}
}
上面代碼中,foo 不再是一個單純的內(nèi)聯(lián)函數(shù) inlining(譯者注:這里指的是解析器可以做內(nèi)聯(lián)處理),
因為它需要知道它自己和它的調(diào)用者。
這不僅抵消了內(nèi)聯(lián)函數(shù)帶來的性能提升,而且破壞了封裝,因此現(xiàn)在函數(shù)可能要依賴于特定的上下文。
因此強烈建議大家不要使用 arguments.callee 和它的屬性。
ES5 提示: 在嚴格模式下,
arguments.callee會報錯TypeError,因為它已經(jīng)被廢除了。