JavaScript変数はループの外側または内側を宣言しますか?


212

AS3では、パフォーマンスを向上させるために、ループの外側ですべての変数を初期化する必要があると思います。これはJavaScriptにも当てはまりますか?どちらが良い/速い/ベストプラクティスですか?

var value = 0;

for (var i = 0; i < 100; i++)
{
    value = somearray[i];
}

または

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}

7
外側!常に外。
BGerrissen

37
とにかく、JavascriptとAS3の両方で変数宣言が関数スコープにプッシュされないのですか?私が正しければ、それは本当に重要ではありません。
10:13に消費者

3
@Andy-関数本体で宣言する前に割り当ててみましたか?おそらくあなたの先入観があなたを迷わせているのでしょう。JSが解釈される場合、プッシュアップスコープを使用したWRTパフォーマンスは、ループブロック内の余分なサイクルを噛みます。コンパイルされている場合(ほとんどのエンジンで現在行われています)、問題にはなりません。
38:38

2
すばらしい質問です。ありがとう。すべての答えを読んだ後、それが小さなループか一時変数のみかどうか、私はそれらを必要な場所に保持し、パフォーマンスに影響しないと信じています。varが複数回関数内で使用される場合、なぜ関数内それを参照しないと、最終的に、次にFN()の外側に飽和することができるグローバル
ディーンミーハン

3
誰もパフォーマンスを測定しようとしなかったのには驚きです。私はjsperfを作成しました。SafariとFirefoxのループ内で宣言すると、少し速くなるようです
。Chrome

回答:


281

JavaScriptでもActionScriptでも、意味やパフォーマンスに違いはありません

varパーサーのディレクティブであり、実行時に実行されるコマンドではありません。特定の識別子がvar関数本体(*)のどこかで1回以上宣言されている場合、ブロック内でのその識別子の使用はすべてローカル変数を参照します。がループの内側、ループの外側、またはその両方valueとして宣言されているかどうかは関係ありませんvar

したがって、最も読みやすいと思われる方を記述してください。すべての変数を関数の先頭に配置することが常に最良の方法であるというのは、クロックフォードとは同意しません。変数がコードのセクションで一時的に使用される場合は、varそのセクションで宣言することをお勧めします。そのため、セクションは独立しており、コピーして貼り付けることができます。それ以外の場合は、関連付けられているを個別に選択して移動することなく、リファクタリング中に数行のコードを新しい関数にコピーアンドペーストするvarと、誤ってグローバルになります。

特に:

for (var i; i<100; i++)
    do something;

for (var i; i<100; i++)
    do something else;

Crockfordは2番目を削除することをお勧めしますvar(またはvarsとvar i;上記の両方を削除します)。jslintはこれを求めます。ただし、IMOの場合var、関数の上部に簡単に忘れられるコードを追加する代わりに、両方のsを保持し、関連するすべてのコードを一緒に保持する方が保守しやすくなります。

個人的にvarは、同じ関数の他の部分で同じ変数名の別の使用法があるかどうかに関係なく、コードの独立したセクションで変数の最初の割り当てとして宣言する傾向があります。私にとってvarは、まったく宣言しなければならないのは望ましくないJSイボです(変数をデフォルトでローカルに設定する方がいいでしょう)。私は、JavaScriptでANSI C [の古いリビジョン]の制限を複製することも私の義務とは考えていません。

(*:ネストされた関数本体以外)


4
私はまだクロックフォードと一緒にいるかどうかを決めることができません。ブロック内で変数を宣言することは、条件付き関数ステートメントを使用することに少し似ています(これはいたずらです)...しかし、私もあなたの主張に同意します:) #confused
Daniel Vassallo

20
+1 JavaScriptの開発者は他の「Cファミリー」プログラマーが理解できるようにコードを書くべきではないので、私はクロックフォードの推論(ダニエルの回答で引用)に同意しません。私は頻繁に使用するvar内部にあれば、ブロックやループ、それは私に多くの意味がありますので。
Andy E

4
-1 OPは、ループが始まる前に、ループの本体にある変数を宣言する必要があるかどうかを尋ねています。ループのインデックス値は明らかに特殊なケースであり(そして巻き上げられ)、OPをまったく助けません。
mkoistinen

21
ループインデックスは特別なケースではなく、通常の割り当てとまったく同じ方法で処理され、巻き上げられます。
ボビンス

31
+1クロックフォードはこれについては間違っています(他の人もいますが、私は余談です)。var関数の先頭でのみ使用されることを要求することは、偶発的なグローバル変数の作成を要求することです。また、無関係な変数の集合をすべて1つの場所で宣言しても意味がありません。特に、これらの変数の一部が使用されない場合があります。
MooGoo

64

言語にはブロックスコープがなく、関数スコープしかないので、理論的にはJavaScriptに違いはありません。

パフォーマンスの引数についてはわかりませんが、Douglas Crockfordは、varステートメントを関数本体の最初のステートメントにすることを推奨しています。JavaScriptプログラミング言語のコード規約からの引用:

JavaScriptにはブロックスコープがないため、ブロック内で変数を定義すると、他のCファミリー言語に精通したプログラマーを混乱させる可能性があります。関数の上部ですべての変数を定義します。

次の例でわかるように、彼にはポイントがあると思います。関数の先頭で変数を宣言しても、変数iforループブロックのスコープ内に保持されていると読者が誤解することはありません。

function myFunction() {
  var i;    // the scope of the variables is very clear

  for (i = 0; i < 10; i++) {
    // ...
  }
}

8
JSスコープについてOPの真実を伝えるための+1。私はそうでなければ言う答えを反対投票するかどうか疑問に思っています!
スペンダー

1
@Kieranmaine:パフォーマンスに影響しないとは言いませんでした。パフォーマンスとは関係なく、ループの外にそれらを置くことについての議論をしました...パフォーマンスの議論についての言及はありませんが、あなたの答えのいずれにも言及していません:)
Daniel Vassallo

1
@Kieranmaine:そのためのソースはありますか?
Andy E

5
@Kieranmaine:AFAIKは、ループ内で変数を宣言した場合でもecma- / javascript、実行時に変数を上げます。それを「巻き上げ」といいます。したがって、違いはありません。
jAndy

1
ES6 letはこの回答にどのように影響しますか?
jbyrd

58

ECMA-/Javascript言語hoists関数の先頭にどこかで宣言された任意の変数。これは、この言語に他の多くのC言語に似た言語ありfunction scope、そうでないためblock scopeです。
としても知られていますlexical scopeます。

のようなものを宣言した場合

var foo = function(){
    for(var i = 0; i < 10; i++){
    }
};

これは次のhoistedようになります。

var foo = function(){
    var i;
    for(i = 0; i < 10; i++){
    }
}

したがって、パフォーマンスに違いはありません(ただし、ここで完全に間違っている場合は修正してください)。関数の先頭以外の場所で変数を宣言
ないことのはるかに優れた引数は、読みやすさです。内で変数を宣言するfor-loopと、この変数はループ本体内でのみアクセスできるという誤った仮定につながる可能性がありますが、これはまったく間違っています。実際には、現在のスコープ内のどこからでもその変数にアクセスできます。


受け入れられたものと同じ基本的な回答ですが、IMOはより読みやすく、情報として提供されます。良くやった。
Anne Gunn

5
ES6 letはこの回答にどのように影響しますか?
jbyrd

13

来年は、すべてのブラウザにコードをプリコンパイルするJSエンジンが搭載されるため、同じコードブロックを何度も解析し、割り当てを実行することによるパフォーマンスの違いはごくわずかになります。

また、必要がない限り、パフォーマンスを最適化しないでください。最初に変数を必要とする場所に変数を近づけることで、コードをクリーンに保つことができます。否定的な面では、ブロックスコープを持つ言語に慣れている人々は混乱するかもしれません。


6

別の考慮事項は、今我々が持っていることletconstES2015で、あなたが特にループブロックになりましスコープ変数をできることです。したがって、ループの外側で同じ変数が必要でない限り(または各反復が前の反復でその変数に対して行われた操作に依存している場合)、おそらくこれを行うのが望ましいでしょう。

for (let i = 0; i < 100; i++) {
    let value = somearray[i];
    //do something with `value`
}

4

Chromeで簡単なテストを行いました。ブラウザでフィドルを試して、結果を確認してください

  var count = 100000000;
    var a = 0;
    console.log(new Date());

    for (var i=0; i<count; i++) {
      a = a + 1
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
      a = a + 1;
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
        var x;
        x = x + 1;
    }

    console.log(new Date());

結果として、最後のテストには最大8秒かかり、前の2つのテストには約2秒しかかかりません。非常に再現性があり、順序に関係なく。

だから、これは私が常にループの外で変数を宣言する必要があることを証明しています。私にとって奇妙なケースは、私がifor()ステートメントで宣言する最初のケースです。これは、インデックスを事前に宣言する2番目のテストと同じくらい高速なようです。


14
@KP:結果は、自分でテストした場合、または多数の人がそれらを検証した場合にのみ証明されます。@mkoistinen:より公正なテストjsfiddle.net/GM8nkを作成しました。Chrome 5でスクリプトを数回実行した後、一貫した勝者がいないことがわかりました。いくつかの更新後、3つのバリエーションすべてが他のバリエーションよりも優れたパフォーマンスを示しました。私から-1、私は恐れています。これを他のブラウザで実行したいかもしれません。IEとFxは1億回のイタレーションを好まなかった。
Andy E

1
@AndyE。うわー、それでこの単純なテストに基づいて、IEは100Xをもっと吸いますか?=)
mkoistinen

2
結果は私にとっては至る所にあります。時々大きな速度の違いがありますが、明確なクロスブラウザの勝者はありません。変だ。Andyのフィドルはより良いテストだと思いますが、各候補を独自の関数に入れます...確かに元のスクリプトが関数の外で実行される場合、varグローバル変数として宣言されているため、実際には何もテストするべきではありませんとにかくグローバルである。
ボビンス

4
事後1年以上、SHAPOW
sdleihssirhc

2
これは私のものではありませんが、興味のある方もいらっしゃる
m1。

1

JavaScriptは、CまたはC ++によって下部に記述された言語ですが、どちらの言語かはよくわかりません。そして、その目的の1つは、内部メモリを処理する手間を省くことです。CまたはC ++でも、ループ内で変数が宣言されている場合に大量のリソースを消費するかどうかを心配する必要はありません。JavaScriptでそれを心配する必要があるのはなぜですか?


1
CまたはC ++はJavaScriptの下部にある場合があります。ただし、ブラウザはJavaScriptを基になる言語(C、C ++)に変換することを忘れないでください。したがって、パフォーマンスはブラウザに依存します
キラ

3
@Kira実際にはC / C ++に変換されません。JavaScriptは、JSランタイム(つまり、ブラウザーで実行されている仮想マシン)によって実行される一連の命令にコンパイルされます。同じ原則がPythonやRubyなどの他の動的言語にも当てはまります。
アンソニーE

@AnthonyE、情報をありがとう。JSがCまたはC ++に変換することについて確信が持てませんでした。したがって、私は私のコメントに使用されている可能性があります
キラ

0

まあ、それはあなたが達成しようとしていることに依存します... valueループブロック内の一時的な変数だけであると仮定すると、2番目の形式を使用する方がはるかに明確です。また、より論理的で冗長です。


一時変数を含め、すべての変数宣言を上に移動すると、実際に混乱が生じる可能性があることを発見しました。
Daniel Sokolowski、2014年

0

forループの内部または外部で変数を宣言しても、違いはありません。以下は、テストするサンプルコードです。

function a() {
   console.log('Function a() starts');
   console.log(new Date());
    var j;
    for (j=0; j<100000000; j++) {
        var x;
        x = x + 1;
    }
    console.log(new Date());
    console.log('Function a() Ends');
}
a()
function b() {
console.log('Function B() starts');
   console.log(new Date());
    var a;
    var j;
    for (j=0; j<100000000; j++) {
      a = a + 1;
    }
    console.log(new Date());
    console.log('Function B() Ends');
}
b()

結果は私の場合に示されました

Function a() starts
VM121:3 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:9 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:10 Function a() Ends
VM121:14 Function B() starts
VM121:15 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:21 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:22 Function B() Ends

ありがとう-MyFavs.in


どちらの場合も、ループの外でjを宣言します。X_x
ジョン・クテジク

let代わりにChromium 81で試してみましたが、少し遅くなる傾向があります(120対115 ms =〜6%= IMOは重要ではvarありa()ません)。
mikiqex

-1

ここでの質問は、基本的にループ内で変数を宣言することです。これを行うとどうなるか考えてみてください。

var a = 30;
var a = 50;
var a = 60;

これは正しいと思いますか?いいえ...変数を何度も宣言したくないので。ループ内で変数を宣言すると、ループの実行回数を宣言しませんか?言うまでもなく、「厳密な使用」モードになっているときは平手打ちされます。人々は元の質問について考えずにクロックフォードに同意しませんでした。

したがって、変数を最初に宣言することは常に良いことです。1.読みやすくするために、2.適切な習慣を作ること。


1
「ループ内で変数を宣言すると、ループの実行回数を宣言するのではないですか?」<-いいえ、そうではありません。変数宣言は巻き上げられるので、あとは代入だけです。
Matthias

-2

Linux OSでChrome、Firefox、jsperfでテストを実行した後のパフォーマンスに関しては、ループ内の変数の宣言とループ外の変数の宣言の間にパフォーマンスの違いがあるように見えます。これは小さな違いですが、これは反復回数と変数宣言の量によっても悪化します。

したがって、最高のパフォーマンスを得るには、ループの外で変数を宣言することをお勧めします。または、変数をインラインで宣言します。例を参照してください。

// inline
for (var ai = 0, al = 100000000, av; ai < al; ai++) {
    av = av + 1;
}

// outside
var bv;
var bl = 100000000;
for (var bi = 0; bi < bl; bi++) {
    bv = bv + 1;
}

変数 'al'と 'av'がforループ宣言行にあることに注意してください。このインライン宣言により、一貫してパフォーマンスが向上しました。ループの外側の変数の宣言についてもです。ここでも、パフォーマンスの違いはほんのわずかです。

https://jsperf.com/outside-inline-for-loop-ase/1


私にとって、あなたのテストはループの中で与えました。そして、そうではありませんでしたが、違いは小さすぎて結論を出すことができません。
Ulysse BN

変数宣言は巻き上げられるため、実際には違いはありません。
トリンコット2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.