Skip to content

Latest commit

 

History

History
98 lines (62 loc) · 4.68 KB

File metadata and controls

98 lines (62 loc) · 4.68 KB

オブジェクトのarguments

JavaScriptの全ての関数スコープはargumentsと呼ばれる特別な変数にアクセスできます。この変数は関数が受け取った全ての引数を保持する変数です。

注意: argumentsが既にvarや正式なパラメーターにより 関数のスコープが定義されている場合は argumentsオブジェクトは作られません。

argumentsオブジェクトはArrayではありません。これは配列と同じような -lengthプロパティと名付けられています- 文法を持っていますが、Array.prototypeを継承している訳では無いので、実際Objectになります。

この為、argumentspushpopsliceといった通常の配列メソッドは使用する事が出来ません。プレーンなforループのような繰り返しでは上手く動作しますが、通常のArrayメソッドを使いたい場合は本当のArrayに変換しなければなりません。

配列への変換

下のコードはargumentsオブジェクトの全ての要素を含んだ新しいArrayを返します。

Array.prototype.slice.call(arguments);

この変換は遅いです。コードのパフォーマンスに関わる重要な部分での使用は推奨しません

引き数の受け渡し

下記の例はある関数から別の関数に引数を引き渡す際に推奨される方法です。

function foo() {
    bar.apply(null, arguments);
}
function bar(a, b, c) {
    // do stuff here
}

他のテクニックとして、高速で非結合のラッパーとしてcallapply両方を一緒に使用するという物があります。

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が返るようになっています。