JavaScriptの全ての関数スコープはargumentsと呼ばれる特別な変数にアクセスできます。この変数は関数が受け取った全ての引数を保持する変数です。
注意:
argumentsが既にvarや正式なパラメーターにより 関数のスコープが定義されている場合はargumentsオブジェクトは作られません。
argumentsオブジェクトはArrayではありません。これは配列と同じような -lengthプロパティと名付けられています- 文法を持っていますが、Array.prototypeを継承している訳では無いので、実際Objectになります。
この為、argumentsでpushやpop、sliceといった通常の配列メソッドは使用する事が出来ません。プレーンなforループのような繰り返しでは上手く動作しますが、通常のArrayメソッドを使いたい場合は本当のArrayに変換しなければなりません。
下のコードはargumentsオブジェクトの全ての要素を含んだ新しいArrayを返します。
Array.prototype.slice.call(arguments);
この変換は遅いです。コードのパフォーマンスに関わる重要な部分での使用は推奨しません。
下記の例はある関数から別の関数に引数を引き渡す際に推奨される方法です。
function foo() {
bar.apply(null, arguments);
}
function bar(a, b, c) {
// do stuff here
}
他のテクニックとして、高速で非結合のラッパーとしてcallとapply両方を一緒に使用するという物があります。
function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
// "メソッド"の非結合バージョンを作成する
// このメソッドはthis, arg1, arg2...argNのパラメーターを持っている
Foo.method = function() {
// 結果: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);
};
argumentsオブジェクトはゲッターとセッター機能を自身のプロパティと同様に関数の仮パラメーターとして作成します。
結果として、仮パラメーターを変更すると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オブジェクトは、関数の内部の名前宣言と仮パラメーターという2つの例外を常に持ちながら生成されます。これは、使用されているかどうかは関係がありません。
ゲッターとセッターは両方とも常に生成されます。その為これを使用してもパフォーマンスに影響は全くといって言い程ありません。argumentsオブジェクトのパラメーターに単純にアクセスしているような、実際のコードであれば尚更です。
ES5での注意: strictモードでは、これらゲッターとセッターは生成されません。
しかし、一つだけモダンJavaScriptエンジンにおいて劇的にパフォーマンスが低下するケースがあります。そのケースとはarguments.calleeを使用した場合です。
function foo() {
arguments.callee; // この関数オブジェクトで何かする
arguments.callee.caller; // そして関数オブジェクトを呼び出す
}
function bigLoop() {
for(var i = 0; i < 100000; i++) {
foo(); // 通常はインライン展開する
}
}
上記のコードでは、fooは自身と自身の呼び出し元の両方を知らないとインライン展開の対象になる事が出来ません。この事は、インライン展開によるパフォーマンスの向上の機会を失くす事になり、また、特定のコンテクストの呼び出しに依存する関数のせいで、カプセル化が解除されてしまいます。
この為にarguments.calleeを使用または、そのプロパティを決して使用しない事を強く推奨します。
ES5での注意: strictモードでは、
arguments.calleeは推奨されていない為にTypeerrorが返るようになっています。