JSLintエラー「for forの本体はifステートメントでラップする必要があります」とはどういう意味ですか?


242

私のJavaScriptファイルでJSLintを使用しました。それはエラーを投げました:

for( ind in evtListeners ) {

41行目の問題9文字目:プロトタイプから不要なプロパティをフィルター処理するには、forステートメントの本文をifステートメントでラップする必要があります。

これは何を意味するのでしょうか?


5
デフォルトでは、「in」は継承されたプロパティも繰り返し処理します。通常、本体はラップされてif (evtListeners.hasOwnProperty(ind))、処理を所有する(継承されない)プロパティのみに制限します。それでも、継承されたプロパティを含むすべてのプロパティを本当に反復処理したい場合があります。その場合、JSLintは、ループ本体をifステートメントでラップして、本当に必要なプロパティを決定するように強制します。これは、仕事とJSlintの幸せになる: if (evtListeners[ind] !== undefined)
xorcus

1
ほとんどの回答は古くなっています。更新されたソリューションは、stackoverflow.com
a /

回答:


430

まず、ループを使用して配列を列挙しないfor inください。決して。古き良きものを使うfor(var i = 0; i<arr.length; i++)

この背後にある理由は次のとおりです。JavaScriptの各オブジェクトには、という特別なフィールドがありprototypeます。そのフィールドに追加したものはすべて、そのタイプのすべてのオブジェクトからアクセスできるようになります。すべての配列に、filter_0ゼロをフィルターで除去するという新しい関数を追加したいとします。

Array.prototype.filter_0 = function() {
    var res = [];
    for (var i = 0; i < this.length; i++) {
        if (this[i] != 0) {
            res.push(this[i]);
        }
    }
    return res;
};

console.log([0, 5, 0, 3, 0, 1, 0].filter_0());
//prints [5,3,1]

これは、オブジェクトを拡張して新しいメソッドを追加する標準的な方法です。多くのライブラリがこれを行っています。しかし、for in今どのように機能するか見てみましょう:

var listeners = ["a", "b", "c"];
for (o in listeners) {
    console.log(o);
}
//prints:
//  0
//  1
//  2
//  filter_0

見える?それは突然、filter_0が別の配列インデックスであると考えます。もちろん、これは実際には数値インデックスではなく、数値インデックスfor inだけでなく、オブジェクトフィールド全体を列挙します。したがって、すべての数値インデックスとを 列挙しますfilter_0。しかしfilter_0、特定の配列オブジェクトのフィールドではなく、すべての配列オブジェクトがこのプロパティを持っています。

幸いなことに、すべてのオブジェクトにはhasOwnPropertyメソッドがあり、このフィールドが実際にオブジェクト自体に属しているか、それともプロトタイプチェーンから単に継承され、そのタイプのすべてのオブジェクトに属しているかをチェックします。

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}
 //prints:
 //  0
 //  1
 //  2

このコードは配列に対しては期待どおりに動作しますが、配列に対しては絶対に使用for inないでくださいfor each infor in配列のインデックスや値ではなく、オブジェクトのフィールドを列挙することに注意してください。

var listeners = ["a", "b", "c"];
listeners.happy = "Happy debugging";

for (o in listeners) {
    if (listeners.hasOwnProperty(o)) {
       console.log(o);
    }
}

 //prints:
 //  0
 //  1
 //  2
 //  happy

43
for in言語はfor in配列を列挙する順序を保証しないため、配列の反復には使用しないでください。番号順ではない可能性があります。さらに、 `for(i = 0; i <array.length; i ++)スタイルコンストラクトを使用する場合、英数字のプロパティではなく、順番に数値インデックスのみを反復していることを確認できます。
ブルトン語

ありがとう!これを参照として保存します。
nyuszika7h

このJSLintの一部が壊れていると確信していたので、私はもう一度この答えを見ていた。おおよそのコードがありました:for(リスナーではo){if(listeners.hasOwnProperty(i)){console.log(o); }}問題は、バグがあり、変数名をiからoに切り替えて、参照を逃してしまったことです。JSLintは、適切なオブジェクトの適切なプロパティについてhasOwnPropertyをチェックしていることを確認できるほどスマートです。
2011

12
ただし、オブジェクトのプロパティを繰り返し処理しても問題ありません。OPはforが配列に適用されたとは決して言っていません。hasOwnPropertyがベストプラクティスですが、たとえば、オブジェクトが別のオブジェクトを拡張し、オブジェクトと「親」オブジェクトのプロパティの両方を一覧表示したい場合など、それを望まない場合があります。
gotofritz 2012

3
私は、for-inループから人々を怖がらせるのではなく(すごいですが)、ループがどのように機能するかを教育し(この回答では正しく行われます)、Object.defineProperty()何も壊さずにプロトタイプを安全に拡張できるように紹介する必要があると思います。ちなみに、ネイティブオブジェクトのプロトタイプの拡張は、なしで行うべきでありませObject.defineProperty
ロバートロスマン、2015

87

jslintの作者であるDouglas Crockfordは、この問題について何度も書いています(そして話しました)。これをカバーする彼のウェブサイトのこのページにセクションがあります:

ステートメント用

ステートメントのクラスは次の形式にする必要があります。

for (initialization; condition; update) {
    statements
}

for (variable in object) {
    if (filter) {
        statements
    } 
}

最初の形式は、配列および事前に決定可能な反復回数のループで使用する必要があります。

2番目のフォームはオブジェクトで使用する必要があります。オブジェクトのプロトタイプに追加されたメンバーは列挙に含まれることに注意してください。オブジェクトの実際のメンバーを区別するためにhasOwnPropertyメソッドを使用して防御的にプログラムするのが賢明です。

for (variable in object) {
    if (object.hasOwnProperty(variable)) {
        statements
    } 
}

また、クロックフォードはYUIシアターで彼がこれについて語るビデオシリーズを持っています。JavaScriptに関するCrockfordの一連のビデオ/講演は、JavaScriptについて少しでも真剣であるかどうかを確認する必要があります。


21

悪い例:(jsHintはエラーをスローします)

for (var name in item) {
    console.log(item[name]);
}

良い:

for (var name in item) {
  if (item.hasOwnProperty(name)) {
    console.log(item[name]);
  }
}

8

ヴァヴァの答えが目印です。jQueryを使用する場合、$.each()関数がこれを処理するため、使用する方が安全です。

$.each(evtListeners, function(index, elem) {
    // your code
});

5
ここでパフォーマンスを考慮する場合、生のループを回避できる場合は$.each(またはunderscore.js を使用)はお勧めしません。jsperfには実行に値するいくつかの目を見張るような比較テストがあります。_.eachfor
nickb

3
これ(jsperf.com/each-vs-each-vs-for-in/3)は、基本的なプロトフィルターを採用しているため、より現実的です
dvdrtrgn

7

@all-JavaScriptのすべてがオブジェクト()であるため、「オブジェクトでのみ使用」などのステートメントは少し誤解を招きます。さらに、JavaScriptは強く型付けされていないため、1 == "1"はtrueです(1 === "1"はそうではありませんが、Crockfordはこれに大きく関わっています)。JSでの配列のprogromatic概念に関して言えば、定義では型付けが重要です。

@ブレントン-専門用語専門家である必要はありません。「連想配列」、「辞書」、「ハッシュ」、「オブジェクト」、これらのプログラミング概念はすべて、JSの1つの構造に適用されます。これは名前(キー、インデックス)の値のペアであり、値は他のオブジェクト(文字列もオブジェクト)にすることができます。

だから、 new Array()と同じです[]

new Object() にほぼ似ています {}

var myarray = [];

すべてのインデックス(別名キー)は整数でなければならないという制限のある配列である構造体を作成します。また、.push()を介して新しいインデックスを自動的に割り当てることもできます。

var myarray = ["one","two","three"];

本当に経由で対処するのが最善です for(initialization;condition;update){

しかし、どうですか:

var myarray = [];
myarray[100] = "foo";
myarray.push("bar");

これを試して:

var myarray = [], i;
myarray[100] = "foo";
myarray.push("bar");
myarray[150] = "baz";
myarray.push("qux");
alert(myarray.length);
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

おそらく配列の最良の使用法ではなく、物事が常に明確であるとは限らないという単なる例証です。

あなたがあなたのキーを知っていて、それらが整数でない場合は間違いなく、あなたの唯一の配列のような構造オプションはオブジェクトです。

var i, myarray= {
   "first":"john",
   "last":"doe",
   100:"foo",
   150:"baz"
};
for(i in myarray){
    if(myarray.hasOwnProperty(i)){  
        alert(i+" : "+myarray[i]);
    }
}

「これをオブジェクトでのみ使用する」とは、配列やオブジェクトを拡張するその他のオブジェクトでは使用しないことを意味します。そうしないと、指摘したように、すべてがオブジェクトを拡張するため、非常にばかげています
Juan Mendes

'「連想配列」、「辞書」、「ハッシュ」、「オブジェクト」、これらのプログラミング概念はすべて、JSの1つの構造に適用されます。いいえ。それらは異なる言語の異なる概念であり、互いに類似しています。しかし、それらが/まったく同じ/と同じように使用され、同じ目的で使用されていると仮定すると、言語についての知識が少なくなることで回避できる、本当に愚かな間違いを犯すようになります。あなたは作品を使用しています。
ブルトン

2

きっと言うのは少し極端です

...配列を列挙するためにfor inループを使用しないでください。決して。古き良きものを使用(var i = 0; i <arr.length; i ++)

ダグラス・クロックフォード抽出物のセクションを強調表示する価値があります

... 2番目のフォームはオブジェクトで使用する必要があります...

数値インデックスの代わりにキーに名前が付けられている連想配列(別名ハッシュテーブル/ディクショナリ)が必要な場合は、これをオブジェクトとして実装する必要がありますvar myAssocArray = {key1: "value1", key2: "value2"...};

この場合myAssocArray.length、nullが表示され(このオブジェクトには「length」プロパティi < myAssocArray.lengthがないため)、それほど遠くまで到達できません。配列キーは便利なプロパティ(つまり、配列メンバーのIDプロパティまたは名前)となるため、連想配列は多くの状況でパフォーマンス上の利点を提供することを期待しています。配列は、ifステートメントを繰り返し評価して、目的の配列エントリを見つけます。

とにかく、JSLintエラーメッセージの説明にも感謝します。無数の連想配列を処理するときは、「isOwnProperty」チェックを使用します。


1
あなたは深く混乱しています。JavaScriptには「連想配列」などはありません。それは厳密にはphpの概念です。
ブルトン

これは、これらのオブジェクトが持っていないというのは本当だlength財産を、しかし、あなたはそれを別の方法を行うことができます:var myArr = []; myArr['key1'] = 'hello'; myArr['key2'] = 'world';
nyuszika7h

3
@ Nyuszika7Hそれは間違った方法です。整数のインデックス付き配列が必要ない場合はvar myArr = []、を使用しないでください。PHPではvar myArr = {}同じですが、JSでは使用できません。
ファンメンデス

連想「配列」は配列ではありません。
ヴィンセント・マクナブ


0

ちょうどのために/ $各/中についての話題に追加するために、私は中のために対$ .eachを使用するためのjsperfテストケースを追加しました:http://jsperf.com/each-vs-for-in/2

ブラウザー/バージョンによって処理方法は異なりますが、$。eachとストレートフォーインはパフォーマンス面で最も安価なオプションのようです。

for inを使用して連想配列/オブジェクトを反復処理し、目的を把握して他のすべてを無視している場合は、jQueryを使用する場合は$ .eachを使用するか、単にfor(そして改行)を使用します最後の要素であるべきことがわかっているものに到達した)

配列を反復処理して、その中の各キーペアで何かを実行する場合、jQueryを使用しない場合はhasOwnPropertyメソッドを使用し、jQueryを使用する場合は$ .eachを使用する必要があります。

for(i=0;i<o.length;i++)ただし、連想配列が必要ない場合は、常に使用してください... lol chromeは、for inまたは$.each

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.