「for(…in…)」ループでの要素の順序


205

JavaScriptの "for…in"ループは、ハッシュテーブル/要素を宣言された順序でループしますか?順番通りに動作しないブラウザはありますか?
使用したいオブジェクトは一度宣言すると変更されることはありません。

私が持っていると仮定します:

var myObject = { A: "Hello", B: "World" };

そして私はさらにそれらを以下で使用します:

for (var item in myObject) alert(item + " : " + myObject[item]);

最も適切なブラウザでは、「A:「こんにちは」」が「B:「世界」」の前に常に来ることを期待できますか?


61
それらは潜在的なブラウザとバリアントのサブセットのみをテストするためです。今後のブラウザについては言うまでもありません。失敗しないテストがあらゆる種類の具体的な証拠を提供すると仮定することは明らかに間違っています。
James Hughes、

6
私自身の限られたjavascript能力がSO群衆よりも優れているとは思えません。奇妙なブラウザがそこに潜んでいることを誰が知っているのか?また、GChromeにはバグがあり、これは私の簡単な例のケースでは明らかではないことを回答で確認できます。
chakrit 2008


回答:


214

John Resigの引用

現在、すべての主要なブラウザは、オブジェクトのプロパティを定義された順序でループします。いくつかの場合を除いて、Chromeもこれを行います。[...]この動作は、ECMAScript仕様では明示的に定義されていません。ECMA-262のセクション12.6.4:

プロパティを列挙するメカニズムは...実装に依存します。

ただし、仕様は実装とはかなり異なります。ECMAScriptの最新の実装はすべて、オブジェクトプロパティが定義された順序で繰り返されます。このため、Chromeチームはこれをバグとみなし、修正する予定です。

すべてのブラウザーは、数値以外のすべてのプロパティ名を処理するChromeとOperaを除いて、定義の順序尊重します。これらの2つのブラウザでは、プロパティは最初の非数値プロパティの前に順番にプルされます(これは、配列を実装する方法に関係しています)。順序も同じですObject.keys

この例では、何が起こるかを明確にする必要があります。

var obj = {
  "first":"first",
  "2":"2",
  "34":"34",
  "1":"1",
  "second":"second"
};
for (var i in obj) { console.log(i); };
// Order listed:
// "1"
// "2"
// "34"
// "first"
// "second"

これの専門性は、これがいつでも変更される可能性があるという事実ほど重要ではありません。このままでいることに依存しないでください。

つまり、順序が重要な場合は配列を使用します。


3
あんまり。Chromeは他のブラウザと同じ順序を実装していません:code.google.com/p/v8/issues/detail?id
Tim Down

20
「順序が重要な場合は配列を使用する」:JSONを使用している場合はどうですか?
HM2K、2011

11
@ HM2K、同じこと、仕様は「オブジェクトは0個以上の名前と値のペアの順序付けられていないコレクションである」と述べています。JSONはJavaScriptではありません:サーバーは、あなたがそれを渡す順序を尊重する必要はありません(そしておそらく尊重しません)。
Borgar、2011

7
21バージョン以降のFirefoxは、挿入順序を尊重していないようです。
Doc Davluz 2013年

2
この答えはES2015では誤りです。
Benjamin Gruenbaum

54

1年後にこれをぶつけます...

それは2012年であり、主要なブラウザーはまだ異なります。

function lineate(obj){
    var arr = [], i;
    for (i in obj) arr.push([i,obj[i]].join(':'));
    console.log(arr);
}
var obj = { a:1, b:2, c:3, "123":'xyz' };
/* log1 */  lineate(obj);
obj.a = 4;
/* log2 */  lineate(obj);
delete obj.a;
obj.a = 4;
/* log3 */  lineate(obj);

現在のブラウザで要旨またはテスト

Safari 5、Firefox 14

["a:1", "b:2", "c:3", "123:xyz"]
["a:4", "b:2", "c:3", "123:xyz"]
["b:2", "c:3", "123:xyz", "a:4"]

Chrome 21、Opera 12、Node 0.6、Firefox 27

["123:xyz", "a:1", "b:2", "c:3"]
["123:xyz", "a:4", "b:2", "c:3"]
["123:xyz", "b:2", "c:3", "a:4"]

IE9

[123:xyz,a:1,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 

16
ここで何が問題なのですか?仕様にあるとおり、これはキーと値のペアの順序付けされていないリストです。
nickf 2013

5
nickf:実装は、仕様が言うことを実行するという点で正しいです。私は誰もがjavascriptの仕様がそうであることに同意していると思います。まあ、「間違った」という言葉を使いたくないのですが、「非常に愚かで迷惑な」ことはどうですか?:P
2013

10
@boxed:オブジェクトをハッシュマップ(テーブル/何でも)として考えます。ほとんどの言語(Java、Pythonなど)では、これらの種類のデータ構造はソートされません。したがって、これがJavaScriptでも同じケースであることは当然のことであり、仕様が間違ったり、愚かになったりすることはありません。
Felix Kling 2013

9
正直なところ、この回答のポイントはわかりません(申し訳ありません)。プロパティが反復される順序は実装の詳細であり、ブラウザーは異なるJavaScriptエンジンを使用するため、順序が異なることが予想されます。これは変わりません。
Felix Kling 2013

2
最先端の実装では、実際の配列に裏付けられているため、整数のプロパティを数値順に受け取り、名前付きプロパティは、隠されたクラス/形状の定式化により、挿入順に受信されます。変化する可能性があるのは、整数プロパティを最初にリストするか、名前付きプロパティを最初にリストするかです。の追加deleteが興味深いのは、少なくともV8では、オブジェクトがハッシュテーブルによって即座にバックアップされるためです。ただし、V8のハッシュテーブルは挿入順に格納されます。最も興味深い結果がここに...私は一種醜さの彼らはそのオフを引っ張ってやるのだろうか、IEで
Esailija

28

以下からのECMAScript言語仕様、(上のセクション12.6.4 for .. inループ):

プロパティを列挙するメカニズムは実装に依存します。列挙の順序はオブジェクトによって定義されます。

セクション4.3.3(「オブジェクト」の定義):

これは、それぞれがプリミティブ値、オブジェクト、または関数を含む、順序付けされていないプロパティのコレクションです。オブジェクトのプロパティに格納されている関数をメソッドと呼びます。

これは、JavaScript実装全体で一貫した順序で列挙されているプロパティに依存できないことを意味します。(とにかく、言語の実装固有の詳細に依存するのは悪いスタイルです。)

順序を定義したい場合は、それを定義する何かを実装する必要があります。たとえば、オブジェクトにアクセスする前にソートするキーの配列などです。


10

列挙または列挙するオブジェクトの要素は、DontEnumフラグが設定されていないプロパティです。ECMAScript(別名Javascript)標準では、「オブジェクトはプロパティの順序付けられていないコレクションである」と明示的に規定されています(http://www.mozilla.org/js/language/E262-3.pdfセクション8.6を参照)。

すべてのJavascript実装が宣言順に列挙されると想定しても、標準に準拠(つまり安全)することはできません。


2
これが、彼が単に:pを仮定するのではなく、質問をしている理由です
ジェイミー・ペイト

5

プロパティの削除に関しても反復順序は混乱していますが、この場合はIEのみです。

var obj = {};
obj.a = 'a';
obj.b = 'b';
obj.c = 'c';

// IE allows the value to be deleted...
delete obj.b;

// ...but remembers the old position if it is added back later
obj.b = 'bb';
for (var p in obj) {
    alert(obj[p]); // in IE, will be a, bb, then c;
                   // not a, c, then bb as for FF/Chrome/Opera/Safari
}

仕様を変更して反復順序を修正したいという欲求は、http://code.google.com/p/v8/issues/detail?id = 164での議論が何らかの示唆である場合、開発者の間で非常に人気のある欲求のようです。


3

IE6では、順序は保証されません。


2

注文は信頼できません。OperaとChromeはどちらもプロパティのリストを順不同で返します。

<script type="text/javascript">
var username = {"14719":"A","648":"B","15185":"C"};

for (var i in username) {
  window.alert(i + ' => ' + username[i]);
}
</script>

上記のコードは、OperaではB、A、C、ChromeではC、A、Bを示しています。


1

これは本質的な質問には答えませんが、基本的な問題の解決策を提供します。

保存された順序に依存できないと仮定して、プロパティとしてキーと値を持つオブジェクトの配列を使用してみませんか?

var myArray = [
    {
        'key'   : 'key1'
        'value' : 0
    },
    {
        'key'   : 'key2',
        'value' : 1
    } // ...
];

今では、キーが一意であることを確認するのはあなた次第です(これもまたあなたにとって重要であると仮定します。同様に、直接アドレス指定の変更、およびfor(... in ...)はインデックスを 'keys'として返します。

> console.log(myArray[0].key);
key1

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
ペンを参照してくださいために取り組む(...で...)のために JDQ(で@JDQ上)CodePen

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