web-dev-qa-db-ja.com

複数のforループで同じ変数名を使用するのは悪い習慣ですか?

JSHintを使用してJavaScriptコードをリントしていました。コードには、次のように使用される2つのforループがあります。

for (var i = 0; i < somevalue; i++) { ... }

したがって、両方のforループは反復にvar iを使用します。

これで、JSHintは2番目のforループのエラーを表示します:「 'i' is already defined」。私はこれが本当ではないと言うことはできません(明らかにそうです)が、var iはその特定の場所でのみ使用されるため、これは重要ではないと常に思っていました。

このようにforループを使用するのは悪い習慣ですか?私のコードのforループごとに異なる変数を使用する必要があります

//for-loop 1
for (var i = 0; ...; i++) { ... }

//for-loop 2
for (var j = 0; ...; j++) { ... }

または、これは私が無視できるエラーの1つです(それは私のコードを壊さないので、それはまだそれがするはずのことをします)?

JSLint btw。関数の先頭で変数iを定義していないため、最初のforループで検証が停止します(そのため、最初にJSHintに切り替えました)。したがって、この質問の例によると、 JSLintまたはJSHint JavaScript検証を使用する必要がありますか? – JSLintを確認するには、とにかくこのようなforループを使用する必要があります。

...
var i;
...
//for-loop 1
for (i = 0; ...; i++) { ... }
...
//for-loop 2
for (i = 0; ...; i++) { ... }

JSLintとJSHintの両方のエラーを回避する必要があるため、これも私には良さそうです。しかし、次のようにforループごとに異なる変数を使用する必要があるかどうかについて、私は不確かです。

...
var i, j;
...
//for-loop 1
for (i = 0; ...; i++) { ... }
//for-loop 2
for (j = 0; ...; j++) { ... }

だから、これにはベストプラクティスがありますか、上記のコードのいずれかで行くことができますか?つまり、「私の」ベストプラクティスを選択しますか?

61
TimG

変数宣言は、それらが現れるスコープの最上部に引き上げられるため、インタープリターは両方のバージョンを同じ方法で効果的に解釈します。そのため、JSHintとJSLintは、ループ初期化子から宣言を移動することをお勧めします。

次のコード...

for (var i = 0; i < 10; i++) {}
for (var i = 5; i < 15; i++) {}

...は次のように効果的に解釈されます。

var i;
for (i = 0; i < 10; i++) {}
for (i = 5; i < 15; i++) {}

iの宣言は実際には1つしかなく、複数の割り当てがあることに注意してください。同じスコープ内で変数を「再宣言」することはできません。

実際に質問に答えるには...

これにはベストプラクティスがありますか、上記のコードのいずれかを使用できますか?

これをどのように処理するのが最適かについては、さまざまな意見があります。個人的には、JSLintに同意し、各スコープの上部ですべての変数を一緒に宣言すると、コードがより明確になると思います。それがコードの解釈方法なので、動作するように見えるコードを書いてみませんか?

しかし、あなたが観察したように、コードは採用されたアプローチに関係なく機能するため、スタイル/慣習の選択であり、あなたが最も快適だと思う形式を使用できます。

58
James Allardice

JavaScriptの変数は関数スコープです(ブロックスコープではありません)。

var iをループで定義すると、ループ内およびそのループを持つ関数内に残ります。

下記参照、

function myfun() {
    //for-loop 1
    for (var i = 0; ...; i++) { ... }

    // i is already defined, its scope is visible outside of the loop1.
    // so you should do something like this in second loop.

    for (i = 0; ...; j++) { ... }

    // But doing such will be inappropriate, as you will need to remember
    // if `i` has been defined already or not. If not, the `i` would be global variable.
}
6
Jashwant

@ TSCrowderによるコメント でのみ言及されています:環境でサポートされている場合(Firefox、Node.js)、ES6では let宣言 を使用できます=

//for-loop 1
for (let i = 0; ...; i++) { ... }

//for-loop 2
for (let i = 0; ...; i++) { ... }

whichは、for-loop内にスコープを制限します。ボーナス:JSHintは文句を言いません。

6
serv-inc

JSHintがエラーを表示する理由は、JS変数スコープは関数であり、変数宣言は関数の最上部に引き上げられているためです。

Firefoxでは、letキーワードを使用してブロックスコープを定義できますが、現在他のブラウザーではサポートされていません。

letキーワードはECMAScript 6仕様に含まれています。

4
Corneliu

私はこの質問に回答したことを知っていますが、スーパーforループが必要な場合は、次のように記述してください。

var names = ['alex','john','paul','nemo'],
    name = '',
    idx = 0,
    len = names.length;

for(;idx<len;++idx)
{
    name = names[idx];
    // do processing...
}

ここで起こっているいくつかのこと...

  1. 配列の長さはlenに保存されています。これにより、JSがnames.lengthを繰り返すたびに評価しなくなります

  2. idx増分はプリインクリメントです(例:++ idx NOT idx ++)。プレインクリメントは、ポストインクリメントよりもネイティブに高速です。

  3. nameへの参照の保存。これはオプションですが、name変数を頻繁に使用する場合に推奨されます。 names[idx]を呼び出すたびに、配列内のインデックスを見つける必要があります。この検索が線形検索、ツリー検索、ハッシュテーブルのいずれであっても、検索は行われています。したがって、参照を減らすために別の変数に参照を保存します。

最後に、これは私の個人的な好みであり、証拠もパフォーマンス上の利点もありません。ただし、変数を初期化するタイプは常に好きです。 name = '',

4

ベストプラクティスは変数のスコープを減らすことです。そのため、ループの反復変数を宣言する最良の方法は

//for-loop 1
for (var i = 0; ...; i++) { ... }

//for-loop 2
for (var j = 0; ...; j++) { ... }

varで宣言された変数のスコープは知っていますが、ここではコードを読みやすくしています。

1
Nikolay Kostov