Jimmy 的架站筆記

嗨~我是 Jimmy!我可能不是你認識的第 1 個 Jimmy,但一定是最帥的那個。


JavaScript iterate object 的時候到底有沒有順序性

By Jimmy 2025-03-18
發表於 javascript

一直以來,都知道 JavaScript 的 object 在 iterate 的時候並不會保證順序,也因此就一直以為是沒有順序的,直到最近仔細看了 MDN 的文件才發現其實是有某種順序性的。

這裡的順序指的是 iterate object 的順序,包含:for…in, for…of, Object.keys(), Object.values()。

1. for…in 會 iterate 的 properties

要知道 object iterate 的順序,可以從 for…in 是如何 iterate object 來了解(for…of 的 MDN 文件並沒有特別解釋 iterate object 的順序,他請你去參考 for…in 的文件)。

首先看看 for…in 會 iterate 的東西:

不會 iterate 的東西:

const obj = { hello: 'world', fruit: 'apple' }
obj[Symbol('a')] = 'a'

// 透過原型鏈指定屬性
// 相當於 Object.prototype.objParent = 'parent'
obj.__proto__.objParent = 'parent'

console.log(obj)
// {1: 'test', hello: 'world', Symbol(a): 'a'}

for (const key in obj) {
	console.log(key)
}
// 1
// hello
// parent

可以觀察到,如果是用 Symbol 作為 key,那就不會被 iterate

如果透過原型鏈賦予 property,則會被 iterate

2. for…in iterate 的順序(同 for…of)

MDN 裡的文件清楚寫到:

Within each component of the prototype chain, all non-negative integer keys (those that can be array indices) will be traversed first in ascending order by value, then other string keys in ascending chronological order of property creation.

因此 iterate 的順序會是:

  1. 非負整數從小到大的 value

  2. property 插入的順序

const obj = { hello: 'world', 5: 'apple', 1: 'banana', world: 'hello'}

for (const key in obj) {
	console.log(key)
}
// 1
// 5
// hello
// world

可以看到雖然 5 和 1 這兩個 property 插入的順序在 hello 後面,但在 iterate 時,卻是 1 → 5 → hello → world。

會這樣設計的原因其實是:在 JavaScript 中,Array 是透過 Object 繼承而來:

const arr = []
console.log(arr.__proto__.constructor)
// ƒ Array() { [native code] }
console.log(arr.__proto__.__proto__.constructor)
// ƒ Object() { [native code] }

const obj = {}
console.log(arr.__proto__.__proto__ === obj.__proto__)
// true

console.log(typeof(arr))
// object

因此在 iterate array 時,一定是希望 index 從小到大 iterate

const arr = ['apple', 'banana', 'cat']
// array 用 for...in iterate 會取得 index 
for (const index in arr) {
	console.log(index)
}
// 1
// 2
// 3

3. 結論

用 for…in 或是 for…of iterate object 時,順序會是:非負整數的 key 從小到大,接著才會是按照插入順序排序。

因此如果你希望你的 object 在 iterate 時,會嚴格按照插入排序的話,那你應該要用 Map,避免一些不預期的錯誤。

const map = new Map([
	['hello', 'world'],
	['5', 'apple'],
	['1', 'banana'],
	['world', 'hello']]
)
console.log(map)
// Map(4) {'hello' => 'world', '5' => 'apple', '1' => 'banana', 'world' => 'hello'}
for (const [key, value] of map) {
	console.log(key)
}
// hello
// 5
// 1
// world

4. Reference

Object.keys() - JavaScript - MDN Web Docs
for…in - JavaScript | MDN - MDN Web Docs
for…of - JavaScript | MDN - MDN Web Docs - Mozilla


你可能也會喜歡

【JavaScript基礎】:Array Methods — 陣列方法整理

【JavaScript基礎】:Array Methods — 陣列方法整理

陣列(array)屬於物件(object)的一種,在Javacript中有許多陣列方法(array methods)可做轉換或運算,來整理一下在看到陣列常見和相關的方法。 1. Array and String:陣列與字串的轉換 字串與陣列相關的方法 1.1 字串轉換成陣列 1.1.1 **Array.from()** 這個方法不只可以轉換字串,還可以轉換類陣列等等。 語法: > Array.f

Read More
【 JavaScript 】JavaScript 費氏數列的 3 種解法

【 JavaScript 】JavaScript 費氏數列的 3 種解法

面試的時候經常會問到怎麼用 JavaScript 解費氏數列,面試到後面整理了一下常見的 3 種解法: 首先說明一下費氏數列:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] ,通常會請你用 JavaScript 求解陣列的最後一個值。 1. JavaScript 費氏數列 - 遞迴解 2. JavaScript 費氏數列 - 迴圈解 3. JavaScript 費氏數列 - Memoization

Read More