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

今天想聊一個剛入門 JavaScript 時很容易卡住的小坑:物件(object)到底怎麼跑迴圈?
陣列有一卡車好用的方法可以用:forEach、map、reduce、find⋯⋯資料是陣列的時候,要遍歷、轉換、篩選都很順手。但實戰裡資料不會永遠那麼乖,尤其是接後端 API 或第三方服務回傳的東西,很常是「以 id 當 key」的物件結構。這時候你伸手就想 .forEach,結果——直接報錯給你看。
為什麼物件不能直接 forEach
我們先看一份常見的資料,這種「拿 id 當鍵」的格式在 API 回傳裡超常出現:
const people = {
'001': { name: 'Casper', like: '鍋燒意麵', age: 18 },
'002': { name: 'Wang', like: '炒麵', age: 24 },
'004': { name: '滷蛋', like: '蘿蔔泥', age: 3 },
};如果你習慣性地對它 forEach:
people.forEach((item) => console.log(item));
// TypeError: people.forEach is not a function爆了。原因很單純:forEach、map 這些方法是掛在 Array.prototype 上的,物件身上根本沒有,自然叫不動。
那以前我們怎麼處理?大多是用 for...in 去列舉物件的鍵。但 for...in 有個惱人的地方——它會連原型鏈上繼承來的屬性一起翻出來,所以課本都會教你再補一句 hasOwnProperty 把不該出現的東西擋掉。一個簡單的遍歷,寫起來卻要防東防西,實在不夠俐落。
換個思路:先把物件變成陣列
與其想辦法「直接遍歷物件」,不如換個更聰明的角度:既然陣列的方法那麼好用,那我把物件先轉成陣列,不就什麼方法都能用了嗎?
ES6 剛好給了我們三把鑰匙,全都掛在 Object 上,傳入物件就會吐回一個陣列:
Object.values— 只拿值Object.keys— 只拿鍵Object.entries— 鍵和值一起拿
下面一個一個帶過去。
只要值的時候:Object.values
Object.values() 把物件裡的「值」全部抽出來,排成一個純陣列:
Object.values(people);
// [
// { name: 'Casper', like: '鍋燒意麵', age: 18 },
// { name: 'Wang', like: '炒麵', age: 24 },
// { name: '滷蛋', like: '蘿蔔泥', age: 3 },
// ]變成陣列之後,剩下的你就熟到不行了——前面接上 Object.values,後面想接什麼陣列方法都行:
Object.values(people).forEach((person) => {
console.log(person.name); // Casper、Wang、滷蛋
});寫起來幾乎和操作純陣列沒兩樣,只是多包了一層 Object.values 而已。這也是我自己最常用的寫法,因為很多情況下我只在乎內容,不在乎那串 001、002 的鍵。
它唯一的限制是:你拿不到 key。如果迴圈裡需要那組編號,就要看下面兩位了。
需要鍵的時候:Object.keys
顧名思義,Object.keys() 抽出來的是「鍵」組成的陣列:
Object.keys(people);
// ['001', '002', '004']那拿到 key 之後怎麼回頭取值?很簡單,JavaScript 裡用 物件[key] 就能撈回對應的 value,所以一個迴圈鍵值都到手:
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] 這樣的小陣列:
Object.entries(people);
// [
// ['001', { name: 'Casper', ... }],
// ['002', { name: 'Wang', ... }],
// ['004', { name: '滷蛋', ... }],
// ]遍歷的時候,搭配解構賦值會讓程式碼漂亮很多:
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.keys或Object.entries二選一,看你順手。
我自己的習慣是偏好 Object.values 和 Object.keys,語法單純、心智負擔低。Object.entries 我會留給「真的需要鍵值成對處理」的場合,例如把物件丟進 map 去產生另一份對照表的時候。
說真的,這幾招用順之後,你大概再也不會想回去寫 for...in 加 hasOwnProperty 那套了——一行就解決,乾淨又安全。下次接到 API 回傳一坨物件、又得跑迴圈時,先想想「我需要的是 key、value、還是兩個都要」,答案自然就浮出來了。
如果你也在寫程式、想找更多即開即用的小工具邊做邊學,歡迎逛逛我整理的工具總覽,都是免費、在瀏覽器本機跑、不囉嗦的那種。我們下篇見!