JavaScriptはオブジェクトプロパティの順序を保証しますか?


647

このようなオブジェクトを作成した場合:

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";

結果のオブジェクトは常にこのようになりますか?

{ prop1 : "Foo", prop2 : "Bar" }

つまり、プロパティは追加したのと同じ順序になりますか?





1
@TJCrowder受け入れられた回答が正確ではなくなった理由についてもう少し詳しく説明してもらえますか?あなたがリンクした質問は、プロパティの順序がまだ仕様ごとに保証されていないという考えに要約されているようです。
zero298

2
@ zero298:その質問に対する受け入れられた回答は、ES2015 +で指定されたプロパティの順序を明確に説明しています。レガシー操作(for-inObject.keys)は(公式に)サポートする必要はありませんが、現在注文があります。(非公式:Firefox、Chrome、およびEdgeはすべて、for-inおよびObject.keysでも指定された順序に従いますが、公式には必要ありません:jsfiddle.net/arhbn3k2/1
TJ Crowder

回答:


474

オブジェクトの反復順序は、ES2015以降の特定のルールセットに従いますが、(常に)挿入順序に従いません。簡単に言うと、反復順序は文字列キーの挿入順序と数字のようなキーの昇順の組み合わせです。

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }

これを実現するには、配列またはMapオブジェクトを使用する方が良い方法です。Mapはいくつかの類似点を共有し、例外なく、挿入順にキーが反復されることObject保証します

Mapのキーは順序付けされていますが、オブジェクトに追加されたキーは順序付けされていません。したがって、それを反復すると、Mapオブジェクトは挿入順にキーを返します。(ECMAScript 2015の仕様では、オブジェクトは文字列キーとシンボルキーの作成順序を保持するため、文字列キーのみを含むオブジェクトをトラバースすると、挿入順にキーが生成されることに注意してください)

注記として、オブジェクトのプロパティの順序はES2015以前はまったく保証されていませんでした。ECMAScript Third Edition(pdf)のオブジェクトの定義:

4.3.3オブジェクト

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


整数キーの動作は、すべてのブラウザーで一貫しているわけではありません。古いブラウザの中には、整数キーを(文字列キーを使用して)挿入順に反復するものと、昇順で反復するものがあります。
Dave Dopson、

1
@DaveDopson-正しい-廃止されたブラウザーは更新されていないため、現在の仕様に従っていません。
TJ Crowder

199

YES(非整数キーの場合)。

ほとんどのブラウザは、オブジェクトのプロパティを次のように繰り返します。

  1. 昇順の整数キー(および「1」のように、intとして解析される文字列)
  2. 文字列キー、挿入順(ES2015はこれを保証し、すべてのブラウザーがこれに準拠します)
  3. シンボル名、挿入順(ES2015はこれとすべてのブラウザーがこれに準拠することを保証します)

一部の古いブラウザは、カテゴリ#1と#2を組み合わせて、すべてのキーを挿入順に繰り返します。キーが整数として解析される可能性がある場合は、特定の反復順序に依存しないことが最善です。

現在の言語仕様(ES2015以降)の挿入順序は保持されます。ただし、整数として解析されるキー(たとえば、「7」または「99」)の場合を除き、ブラウザ間で動作が異なります。たとえば、キーが数値として解析される場合、Chrome / V8は挿入順序を考慮しません。

古い言語仕様(ES2015より前):反復順序は技術的に未定義でしたが、すべての主要なブラウザーはES2015の動作に準拠していました。

ES2015の動作は、言語仕様が既存の動作によって駆動される好例であり、その逆ではないことに注意してください。下位互換性の考え方の詳細については、http: //code.google.com/p/v8/issues/detail?id=164を参照してください。これは、Chromeの反復順序動作の背後にある設計上の決定を詳細にカバーするChromeのバグです。 。そのバグレポートに関する(むしろ意見のある)コメントの1つによると、

標準は常に実装に従います。これはXHRの元であり、GoogleはGearsを実装してから同等のHTML5機能を採用することで同じことを行います。適切な修正は、ECMAに、事実上の標準動作を仕様の次のリビジョンに正式に組み込むことです。


2
@BenjaminGruenbaum-それはまさに私のポイントでした。2014年の時点で、すべての主要ベンダーは共通の実装を行っているため、標準は最終的に従います(つまり、2015年)。
Dave Dopson、2015年

2
ところで:リアクトcreateFragmentAPIはすでにこれに依存しています...🤔
mik01aj

7
@BenjaminGruenbaumあなたのコメントは偽です。ES2015では、順序は選択されたメソッドに対してのみ保証されます。以下のftorの回答を参照してください。
Piotr Dobrogost

82

通常のオブジェクトのプロパティの順序は、JavaScriptの複雑なテーマです。

ES5では明示的に順序が指定されていませんが、ES2015では特定の場合に順序があります。次のオブジェクトがあります。

o = Object.create(null, {
  m: {value: function() {}, enumerable: true},
  "2": {value: "2", enumerable: true},
  "b": {value: "b", enumerable: true},
  0: {value: 0, enumerable: true},
  [Symbol()]: {value: "sym", enumerable: true},
  "1": {value: "1", enumerable: true},
  "a": {value: "a", enumerable: true},
});

これにより、次の順序になります(特定の場合)。

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. 昇順の整数のようなキー
  2. 挿入順の通常のキー
  3. 挿入順の記号

したがって、3つのセグメントがあり、(例で発生したように)挿入順序を変更する可能性があります。そして、整数のようなキーは挿入順序にまったく固執しません。

問題は、ES2015仕様でこの順序が保証されているメソッドは何ですか?

次のメソッドは、表示される順序を保証します。

  • Object.assign
  • Object.defineProperties
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys

次のメソッド/ループは、順序をまったく保証しません。

  • Object.keys
  • のために
  • JSON.parse
  • JSON.stringify

結論:ES2015でも、JavaScriptの通常のオブジェクトのプロパティの順序に依存するべきではありません。エラーが発生しやすい。Map代わりに使用してください。


私はノードv8.11で結論を大まかにテストしましたが、それは正しいです。
merlin.ye 2018

1
@BenjaminGruenbaumどんな考え?
evolutionxbox

Object.entriesは整数規則に従って順序を保証します
Mojimi

1
「ES2015であっても、JavaScriptの通常のオブジェクトのプロパティの順序に依存するべきではありません。エラーが発生しやすくなります。代わりにマップを使用してください。」コンプライアンスが複雑な場合、物件の注文に依存することはできません。古いブラウザをサポートする必要がある場合、下位互換性についてこれをどのようにテストすればよいでしょうか?
zero298

1
それに関する公式のドキュメントやリファレンスはありますか?
ビート

66

執筆時点では、ほとんどのブラウザーは挿入されたときと同じ順序でプロパティを返しましたが、これは動作が明示的に保証されていなかったため、信頼されるべきではありませんでした。

ECMAScript仕様は言うために使用しました:

プロパティを列挙するメカニズムと順序は指定されていません...

ただし、ES2015以降では、整数以外のキーは挿入順に返されます。


16
Chromeは他のブラウザとは異なる順序を実装しています。code.google.com/p/v8/issues/detail?id=164を
ティムダウン

9
Opera 10.50以上、およびIE9はChromeの注文と一致しています。FirefoxとSafariは少数派になりました(両方ともオブジェクト/配列の順序が異なります)。
gsnedders

1
@Veverkeは、順序に明示的な保証がないため、順序は事実上ランダムであると常に想定する必要があります。
Alnitak 2015年

1
@Veverkeいいえ、順序は予測可能なものとは異なります。これは実装に依存しており、いつでも変更される可能性があり、(たとえば)ブラウザが更新されるたびに変更される可能性があります。
アルニタク2015年

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

42

この全体の答えは、特定の時点で、または歴史的にエンジンが何を行っているかではなく、仕様への準拠のコンテキストにあります。

一般的には、

実際の質問は非常にあいまいです。

プロパティは、追加したのと同じ順序になります

どのような状況で?

答えは次のとおりです。それはいくつかの要因に依存します。一般に、いいえ

時々、はい

ここでは、プレーンのプロパティキーの順序を当てにできますObjects

  • ES2015準拠エンジン
  • 自分のプロパティ
  • Object.getOwnPropertyNames()Reflect.ownKeys()Object.getOwnPropertySymbols(O)

すべての場合において、これらのメソッドには、列挙できないプロパティキーと順序キーが含まれます[[OwnPropertyKeys]](以下を参照)。それらには、含まれるキー値のタイプが異なります(Stringおよび/またはSymbol)。このコンテキストでStringは整数値が含まれます。

Object.getOwnPropertyNames(O)

O独自のStringキー付きプロパティ(プロパティ名)を返します。

Reflect.ownKeys(O)

O独自のString-およびSymbol-keyedプロパティを返します。

Object.getOwnPropertySymbols(O)

Oの独自のSymbolキー付きプロパティを返します。

[[OwnPropertyKeys]]

順序は基本的に次のとおりです。Strings昇順の整数のような、非整数のようなStrings、作成順の、作成順のシンボルです。これを呼び出す関数によっては、これらのタイプの一部が含まれていない場合があります。

特定の言語では、キーは次の順序で返されます。

  1. ... 整数インデックスPであるO[反復対象のオブジェクト]の独自の各プロパティキー(数値インデックスの昇順)

  2. ...それぞれの独自のプロパティキーPO文字列ですが、プロパティの作成順に整数のインデックスではありません

  3. ... プロパティの作成順に、それぞれの独自のプロパティキーPOシンボルです

Map

順序付けられたマップに関心がある場合Mapは、プレーンではなくES2015で導入されたタイプの使用を検討する必要がありObjectsます。



7

ES2015では、それは可能ですが、あなたが思うかもしれない

オブジェクトのキーの順序は、ES2015まで保証されませんでした。それは実装定義でした。

しかし、ES2015内にされた指定されました。JavaScriptの多くのことと同様に、これは互換性のために行われ、ほとんどのJSエンジンの間の既存の非公式の標準を反映しています(例外として、あなたが知っている人は知っています)。

順序は仕様で、抽象オペレーションOrdinaryOwnPropertyKeysの下で定義されています。これは、オブジェクト自体のキーを反復するすべてのメソッドを支えています。言い換えると、順序は次のとおりです。

  1. すべての整数インデックスのキー(のようなもの"1123""55"数値の昇順で、など)。

  2. 整数インデックスではないすべての文字列キー。作成順(古い順)。

  3. すべてのシンボルキー(作成順(古い順))。

順序が信頼できないと言うのはばかげています-それは信頼できます、それはおそらくあなたが望むものではないでしょう、そして現代のブラウザはこの順序を正しく実装します。

一部の例外には、for .. inループなど、継承されたキーを列挙するメソッドが含まれます。for .. inループは、仕様に応じて順序を保証するものではありません。


7

ES2015以降、プロパティを反復する特定のメソッドでプロパティの順序が保証されています。しかし、他の人ではありません。残念ながら、順序を持つことが保証されていないメソッドは、一般的に最も頻繁に使用されます。

  • Object.keysObject.valuesObject.entries
  • for..in ループ
  • JSON.stringify

ただし、ES2020では、これらの以前は信頼できなかったメソッドのプロパティの順序は完成した提案であるfor-in mechanics により、仕様によって他と同じ決定論的な方法で反復されることが保証されます。

保証された反復順序を持つメソッド(Reflect.ownKeysおよびなどObject.getOwnPropertyNames)と同様に、以前に指定されていないメソッドも次の順序で反復します。

  • 数値配列キー(昇順)
  • 他のすべての非記号キー、挿入順
  • 記号キー、挿入順

これは、ほとんどすべての実装がすでに行っている(そして何年もの間行ってきた)ものですが、新しい提案によって正式になりました。

現在の仕様は、「ほとんど完全に未指定ですが、反復順序で残されていますが、実際のエンジンはより一貫している傾向があります:」

ECMA-262の特異性の欠如は、現実を反映していません。何年も前の議論で、実装者はfor-inの動作にいくつかの制約があり、Web上でコードを実行したい人なら誰でも従う必要があることに気づきました。

すべての実装はプロパティを繰り返し予測可能に反復しているため、下位互換性を損なうことなく仕様に組み込むことができます。


現在実装が合意していないいくつかの奇妙なケースがあり、そのような場合、結果の順序は不特定のままになります。プロパティの順序を保証するには

反復されるオブジェクトも、そのプロトタイプチェーンの何も、プロキシ、型付き配列、モジュール名前空間オブジェクト、またはホストエキゾチックオブジェクトではありません。

オブジェクトもそのプロトタイプチェーンの何も、反復中にプロトタイプが変更されません。

オブジェクトもそのプロトタイプチェーンの何も、反復中に削除されたプロパティを持ちません。

オブジェクトのプロトタイプチェーンには、反復中に追加されるプロパティはありません。

オブジェクトのプロパティやプロトタイプチェーンの何も、反復中に列挙可能性が変化しません。

列挙不可能なプロパティは、列挙可能なプロパティをシャドウしません。


5

他の人が述べたように、オブジェクトのプロパティを反復するときの順序は保証されません。複数のフィールドの順序付きリストが必要な場合は、オブジェクトの配列を作成することをお勧めします。

var myarr = [{somfield1: 'x', somefield2: 'y'},
{somfield1: 'a', somefield2: 'b'},
{somfield1: 'i', somefield2: 'j'}];

この方法では、通常のforループを使用して挿入順序を指定できます。次に、必要に応じて、Array sortメソッドを使用して、これを新しい配列にソートできます。


3

これを難しい方法で見つけました。

ReactとReduxを使用すると、子を生成するためにトラバースしたいキーの状態コンテナーが、ストアが変更されるたびに更新されます(Reduxの不変性の概念に従って)。

したがって、Object.keys(valueFromStore)私はを使用Object.keys(valueFromStore).sort()するために、少なくともキーがアルファベット順になっているようにしました。


-7

JSON標準から:

オブジェクトは、0個以上の名前と値のペアの順序付けられていないコレクションです。名前は文字列で、値は文字列、数値、ブール値、null、オブジェクト、または配列です。

(強調鉱山)。

したがって、注文を保証することはできません。


7
これは、JSON仕様ではなく、ECMAScript標準によって指定されます。
Alnitak

7
@ Alnitak、@ Iacqui:JSONはこれをECMAScript仕様からのみ取得します。JSONにも指定されていますが、これは実際には質問とは関係ありません。
パウロEbermann
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.