Object.create(null)

TypeError: Cannot convert object to primitive value

JavaScript のオブジェクトのキーや要素の列挙順は保証されているんですか

A. 場合によります

オブジェクトのキーまたは要素の列挙といえば次の 2 つくらいでしょう. たぶん.

  • Object.keys, Object.values, Object.entries など Object のメソッド
  • for-in

for-of は直接オブジェクトのキーや要素を列挙することはないのでここでは関係ないです.

lodash などの各種ライブラリにあるような列挙用のメソッドについてはそれらのドキュメントを参照してください. JavaScript の仕様と関係ないので.

Object のメソッド

仕様は以下.

どれも EnumerableOwnPropertyNames というのを呼び出していますね.

https://www.ecma-international.org/ecma-262/9.0/index.html#sec-enumerableownpropertynames

これを見ると [[OwnPropertyKeys]] の順番がそのまま出てくるようです. 通常のオブジェクトだとどうなっているか見てみましょう.

1. Let keys be a new empty List.
2. For each own property key P of O that is an integer index, in ascending numeric index order, do
 a. Add P as the last element of keys.
3. For each own property key P of O that is a String but is not an integer index, in ascending chronological order of property creation, do
 a. Add P as the last element of keys.
4. For each own property key P of O that is a Symbol, in ascending chronological order of property creation, do
 a. Add P as the last element of keys.
5. Return keys.

というわけで「整数キーが数値昇順, 文字列キーが作成順, Symbol キーが作成順」という順番に取得されます.

JS のオブジェクトのキーは全て文字列では? と思われた方もいることでしょう. それはそれで正しいのですが, 見ての通り整数を表す文字列のキーは特別扱いされています.

for-in

このへんですね.

https://www.ecma-international.org/ecma-262/9.0/index.html#integer-index

EnumerateObjectProperties というのを使っています.

https://www.ecma-international.org/ecma-262/9.0/index.html#sec-enumerate-object-properties

The mechanics and order of enumerating the properties is not specified

とあるので順番は定められていません. キーの取得には上でも出てきた [[OwnPropertyKeys]] を必ず使うことになっているので, まあ普通に実装したら順番そのままになりそうですが, シャッフルされていても文句は言えません.

結論

  • Object.keys, Object.values, Object.entries など Object のメソッドでは, 「整数キーが数値昇順, 文字列キーが作成順, Symbol キーが作成順」という順番が保証されている
  • for-in では順番は保証されていない

とはいえ順番が保証されているからといって不必要にそれに依存しない方がベター, というのは言うまでもないですよね.