JS 中 Object 的 keys 是無序的嗎

來自 「蔡昕萌」 同學的內部分享。

在最開始學習 JavaScript 時,我一直被灌輸 Object 中的 Key 是無序的,不可靠的,而與之相對的是 Map 實例會維護鍵值對的插入順序。

「But,Object 的鍵值對真的是無序的嗎?」 實際上在 ES2015 以後,Object.keys 的規則變了:

在一些現代的瀏覽器中,keys 輸出順序是可以預測的!

Key 都爲自然數:

注意這裏的自然數是指正整數或 0,如果是其他類的 Number —— 浮點數或者負數 —— 都會走到下一組類型裏,像NaN或者Infinity這種也自然歸到下一個類型裏,但是像科學記數法這個會稍微特殊一點,感興趣的同學可以自己試一下。

總結來說,就是當前的 key 如果是自然數就按照自然數的大小進行升序排序。

const objWithIndices = {
  23: 23,
  '1': 1,
  1000: 1000
};

console.log(Reflect.ownKeys(objWithIndices)); // ["1", "23", "1000"]
console.log(Object.keys(objWithIndices)); // ["1", "23", "1000"]
console.log(Object.getOwnPropertyNames(objWithIndices)); // ["1", "23", "1000"]

包括在 for-in 循環的遍歷中,keys 也是按照這個順序執行的。

Key 都爲 String:

如果 key 是不爲自然數的 String(Number 也會轉爲 String)處理,則按照加入的時間順序進行排序。

const objWithStrings = {
  "002": "002",
  c: 'c',
  b: "b",
  "001": "001",
}

console.log(Reflect.ownKeys(objWithStrings)); // ["002", "c", "b", "001"]
console.log(Object.keys(objWithStrings));// ["002", "c", "b", "001"]
console.log(Object.getOwnPropertyNames(objWithStrings));// ["002", "c", "b", "001"]

Key 都爲 symbol

const objWithSymbols = {
  [Symbol("first")]: "first",
  [Symbol("second")]: "second",
  [Symbol("last")]: "last",
}

console.log(Reflect.ownKeys(objWithSymbols));// [Symbol(first), Symbol(second), Symbol(last)]
console.log(Object.keys(objWithSymbols));// [Symbol(first), Symbol(second), Symbol(last)]
console.log(Object.getOwnPropertyNames(objWithSymbols));// [Symbol(first), Symbol(second), Symbol(last)]

如果 Key 都爲 Symbol,順序和 String 一樣,也是按照添加的順序進行排序的。

如果是以上類型的相互結合

const objWithStrings = {
  "002": "002",
  [Symbol("first")]: "first",
  c: "c",
  b: "b",
  "100": "100",
  "001": "001",
  [Symbol("second")]: "second",
}

console.log(Reflect.ownKeys(objWithStrings));
// ["100", "002", "c", "b", "001", Symbol(first), Symbol(second)]

結果是先按照自然數升序進行排序,然後按照非數字的 String 的加入時間排序,然後按照 Symbol 的時間順序進行排序,也就是說他們會先按照上述的分類進行拆分,先按照自然數、非自然數、Symbol 的順序進行排序,然後根據上述三種類型下內部的順序進行排序。

Recap

  1. 在 ES6 之前 Object 的鍵值對是無序的;

  2. 在 ES6 之後 Object 的鍵值對按照自然數、非自然數和 Symbol 進行排序,自然數是按照大小升序進行排序,其他兩種都是按照插入的時間順序進行排序。

References:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/qyyrQNC6q6p496OdZIQ6ew