93

Javascript で名前付き関数と匿名関数を使用する場合、パフォーマンスに違いはありますか?

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

めったに使用されない関数でコードが乱雑にならないため、最初の方法はより整然としていますが、その関数を複数回再宣言することは重要ですか?

4

12 に答える 12

93

The performance problem here is the cost of creating a new function object at each iteration of the loop and not the fact that you use an anonymous function:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

You are creating a thousand distinct function objects even though they have the same body of code and no binding to the lexical scope (closure). The following seems faster, on the other hand, because it simply assigns the same function reference to the array elements throughout the loop:

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

If you were to create the anonymous function before entering the loop, then only assign references to it to the array elements while inside the loop, you will find that there is no performance or semantic difference whatsoever when compared to the named function version:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

In short, there is no observable performance cost to using anonymous over named functions.

As an aside, it may appear from above that there is no difference between:

function myEventHandler() { /* ... */ }

and:

var myEventHandler = function() { /* ... */ }

The former is a function declaration whereas the latter is a variable assignment to an anonymous function. Although they may appear to have the same effect, JavaScript does treat them slightly differently. To understand the difference, I recommend reading, “<a href="http://www.dustindiaz.com/javascript-function-declaration-ambiguity/" rel="noreferrer">JavaScript function declaration ambiguity”.

The actual execution time for any approach is largely going to be dictated by the browser's implementation of the compiler and runtime. For a complete comparison of modern browser performance, visit the JS Perf site

于 2008-09-17T09:07:24.707 に答える
23

これが私のテストコードです:

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

結果:
テスト1:142msテスト2:1983ms

JSエンジンは、Test2と同じ関数であることを認識せず、毎回コンパイルしているようです。

于 2008-09-17T07:52:48.410 に答える
3

一般的な設計原則として、同じコードを複数回実装することは避けてください。代わりに、一般的なコードを関数に取り出し、その関数(一般的で、十分にテストされ、変更が簡単)を複数の場所から実行する必要があります。

(質問から推測したものとは異なり)内部関数を1回宣言し、そのコードを1回使用している場合(そしてプログラム内で他に同じものがない場合)、おそらく(推測では)匿名関数は同じように扱われます。通常の名前付き関数としてのコンパイラ。

特定の場合に非常に便利な機能ですが、多くの状況で使用するべきではありません。

于 2008-09-17T07:33:48.143 に答える
1

大きな違いはないと思いますが、違いがある場合は、スクリプトエンジンやブラウザによって異なる可能性があります。

コードを理解しやすい場合は、関数を何百万回も呼び出すことを期待しない限り、パフォーマンスは問題になりません。

于 2008-09-17T07:36:49.900 に答える
0

@nickf

(コメントするだけの担当者がいたらいいのですが、このサイトを見つけたばかりです)

私のポイントは、名前付き/無名関数と、反復で実行+コンパイルするユースケースとの間に混乱があるということです。私が説明したように、anon + namedの違い自体はごくわずかです。つまり、これは誤ったユースケースだと言っています。

私には明らかなようですが、そうでない場合は、「愚かなことをしない」(このユースケースの一定のブロックシフトとオブジェクトの作成が1つです)と確信が持てない場合は、テストしてください。

于 2008-09-17T10:14:49.147 に答える
0

匿名オブジェクトは、名前付きオブジェクトよりも高速です。しかし、より多くの関数を呼び出すことはより費用がかかり、匿名関数を使用することで得られる節約をいくらか上回ります。呼び出される各関数は、呼び出しスタックに追加されます。これにより、わずかですが重要なオーバーヘッドが発生します。

ただし、暗号化/復号化ルーチンなど、パフォーマンスに同様に敏感なものを作成している場合を除いて、他の多くの人が指摘しているように、高速コードよりもエレガントで読みやすいコードを最適化する方が常に優れています。

うまく設計されたコードを書いていると仮定すると、速度の問題は、インタープリター/コンパイラーを書いている人の責任であるはずです。

于 2008-09-17T14:15:10.440 に答える
0

さまざまなブラウザー、特に IE ブラウザーで確実にループを高速化するのは、次のようにループすることです。

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

ループ条件に任意の 1000 を入れましたが、配列内のすべての項目を調べたい場合は、私のドリフトが発生します。

于 2008-09-17T07:44:41.270 に答える
0

@ニックフ

ただし、これはかなり面倒なテストですが、メソッド 1 (N 回コンパイル、JS エンジンによって異なります) とメソッド 2 (1 回コンパイル) のコストが明らかにかかる実行時間とコンパイル時間を比較しています。このような方法でコードを書いて試験に合格する JS 開発者を想像することはできません。

はるかに現実的なアプローチは匿名の割り当てです。実際に document.onclick メソッドに使用しているのは次のようなものであり、実際には anon メソッドをやや支持しています。

あなたと同様のテスト フレームワークを使用します。


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}
于 2008-09-17T08:40:08.093 に答える
0

参照は、ほとんどの場合、それが参照しているものよりも遅くなります。このように考えてみてください - 1 + 1 を足した結果を出力したいとしましょう。

alert(1 + 1);

また

a = 1;
b = 1;
alert(a + b);

これは非常に単純化された見方だと思いますが、説明的なものですよね? 複数回使用される場合にのみ、参照を使用してください。たとえば、次の例のどれがより理にかなっていますか。

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

また

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

2 つ目は、より多くの行がある場合でも、より良い練習です。うまくいけば、これはすべて役に立ちます。(そして、jquery 構文は誰もがっかりしませんでした)

于 2008-09-17T09:11:32.603 に答える
0

はい!無名関数は、通常の関数よりも高速です。おそらく、速度が最も重要である場合...コードの再利用よりも重要である場合は、無名関数の使用を検討してください。

JavaScript と無名関数の最適化に関する非常に優れた記事がここにあります。

http://dev.opera.com/articles/view/effective-javascript/?page=2

于 2008-09-17T09:32:45.363 に答える