Skip to content

JavaScript 物件迴圈怎麼跑?Object.keys / values / entries 一次搞懂

JavaScript 物件迴圈怎麼跑?Object.keys / values / entries 一次搞懂

今天想聊一個剛入門 JavaScript 時很容易卡住的小坑:物件(object)到底怎麼跑迴圈?

陣列有一卡車好用的方法可以用:forEachmapreducefind⋯⋯資料是陣列的時候,要遍歷、轉換、篩選都很順手。但實戰裡資料不會永遠那麼乖,尤其是接後端 API 或第三方服務回傳的東西,很常是「以 id 當 key」的物件結構。這時候你伸手就想 .forEach,結果——直接報錯給你看。

為什麼物件不能直接 forEach

我們先看一份常見的資料,這種「拿 id 當鍵」的格式在 API 回傳裡超常出現:

js
const people = {
  '001': { name: 'Casper', like: '鍋燒意麵', age: 18 },
  '002': { name: 'Wang', like: '炒麵', age: 24 },
  '004': { name: '滷蛋', like: '蘿蔔泥', age: 3 },
};

如果你習慣性地對它 forEach

js
people.forEach((item) => console.log(item));
// TypeError: people.forEach is not a function

爆了。原因很單純:forEachmap 這些方法是掛在 Array.prototype 上的,物件身上根本沒有,自然叫不動。

那以前我們怎麼處理?大多是用 for...in 去列舉物件的鍵。但 for...in 有個惱人的地方——它會連原型鏈上繼承來的屬性一起翻出來,所以課本都會教你再補一句 hasOwnProperty 把不該出現的東西擋掉。一個簡單的遍歷,寫起來卻要防東防西,實在不夠俐落。

換個思路:先把物件變成陣列

與其想辦法「直接遍歷物件」,不如換個更聰明的角度:既然陣列的方法那麼好用,那我把物件先轉成陣列,不就什麼方法都能用了嗎?

ES6 剛好給了我們三把鑰匙,全都掛在 Object 上,傳入物件就會吐回一個陣列:

  • Object.values — 只拿值
  • Object.keys — 只拿鍵
  • Object.entries — 鍵和值一起拿

下面一個一個帶過去。

只要值的時候:Object.values

Object.values() 把物件裡的「值」全部抽出來,排成一個純陣列:

js
Object.values(people);
// [
//   { name: 'Casper', like: '鍋燒意麵', age: 18 },
//   { name: 'Wang', like: '炒麵', age: 24 },
//   { name: '滷蛋', like: '蘿蔔泥', age: 3 },
// ]

變成陣列之後,剩下的你就熟到不行了——前面接上 Object.values,後面想接什麼陣列方法都行:

js
Object.values(people).forEach((person) => {
  console.log(person.name); // Casper、Wang、滷蛋
});

寫起來幾乎和操作純陣列沒兩樣,只是多包了一層 Object.values 而已。這也是我自己最常用的寫法,因為很多情況下我只在乎內容,不在乎那串 001002 的鍵。

它唯一的限制是:你拿不到 key。如果迴圈裡需要那組編號,就要看下面兩位了。

需要鍵的時候:Object.keys

顧名思義,Object.keys() 抽出來的是「鍵」組成的陣列:

js
Object.keys(people);
// ['001', '002', '004']

那拿到 key 之後怎麼回頭取值?很簡單,JavaScript 裡用 物件[key] 就能撈回對應的 value,所以一個迴圈鍵值都到手:

js
Object.keys(people).forEach((id) => {
  console.log(id);          // '001'
  console.log(people[id]);  // { name: 'Casper', ... }
});

我個人很愛這個寫法,因為語意直白、好讀,要 key 有 key、要 value 用 people[id] 順手就拿到了。

鍵值一起拿:Object.entries

Object.entries() 算是前兩者的綜合版,一次把 key 和 value 都給你,每一筆都是 [key, value] 這樣的小陣列:

js
Object.entries(people);
// [
//   ['001', { name: 'Casper', ... }],
//   ['002', { name: 'Wang', ... }],
//   ['004', { name: '滷蛋', ... }],
// ]

遍歷的時候,搭配解構賦值會讓程式碼漂亮很多:

js
Object.entries(people).forEach(([id, person]) => {
  console.log(id, person.name); // '001' 'Casper'
});

唯一要注意的是,因為它多包了一層 [key, value] 結構,沒用解構的話寫起來會稍微繞一點(要靠 item[0]item[1] 取值),可讀性會打折。所以我會建議搭 entries 一定要記得用解構。

實戰上到底該用哪一個?

講了三個,但其實你日常只要記得兩種判斷就夠了

  • 只需要值、不在乎 key → 用 Object.values,最精簡。
  • key 和 value 都要Object.keysObject.entries 二選一,看你順手。

我自己的習慣是偏好 Object.valuesObject.keys,語法單純、心智負擔低。Object.entries 我會留給「真的需要鍵值成對處理」的場合,例如把物件丟進 map 去產生另一份對照表的時候。

說真的,這幾招用順之後,你大概再也不會想回去寫 for...inhasOwnProperty 那套了——一行就解決,乾淨又安全。下次接到 API 回傳一坨物件、又得跑迴圈時,先想想「我需要的是 key、value、還是兩個都要」,答案自然就浮出來了。

如果你也在寫程式、想找更多即開即用的小工具邊做邊學,歡迎逛逛我整理的工具總覽,都是免費、在瀏覽器本機跑、不囉嗦的那種。我們下篇見!