JavaScript 物件操作:Object.keys、Object.values 與 Object.entries
簡介
在 JavaScript 中,物件(Object)是最常用的資料結構之一。它以「鍵-值」的形式儲存資訊,讓我們可以隨時透過鍵名取得對應的值。雖然直接存取單一屬性相當簡單,但在實務開發中,我們常常需要遍歷整個物件、取得所有鍵或值,甚至同時取得鍵和值來進行資料處理。
Object.keys、Object.values 與 Object.entries 正是為了這些需求而設計的三個靜態方法。它們不僅讓程式碼更具可讀性,也能避免手動寫迴圈時可能產生的錯誤。掌握這三個方法,等於掌握了物件資料的「全景視角」,在資料轉換、表單處理、API 回傳結果的解析等情境中,都能大幅提升開發效率。
核心概念
1. Object.keys(obj)
- 功能:回傳一個陣列,裡面包含物件自身(不含原型鏈)所有可列舉(enumerable)的鍵(屬性名稱)。
- 回傳值類型:
Array<string> - 常見用途:遍歷鍵、計算屬性數量、配合
Array.prototype.map產生新資料結構。
const user = {
name: "Alice",
age: 28,
email: "alice@example.com"
};
const keys = Object.keys(user);
console.log(keys); // ["name", "age", "email"]
註:
Object.keys只會列出可列舉的自有屬性,Symbol型別的鍵不會被包含。
2. Object.values(obj)
- 功能:回傳一個陣列,裡面包含物件自身所有可列舉屬性的值。
- 回傳值類型:
Array<any> - 常見用途:快速取得所有值進行統計、過濾或映射(map)等操作。
const scores = {
math: 92,
english: 85,
science: 78
};
const values = Object.values(scores);
console.log(values); // [92, 85, 78]
// 計算平均分數
const avg = values.reduce((sum, v) => sum + v, 0) / values.length;
console.log(`平均分數:${avg}`); // 平均分數:85
3. Object.entries(obj)
- 功能:回傳一個二維陣列,每個子陣列都是
[key, value]形式,代表一組鍵值對。 - 回傳值類型:
Array<[string, any]> - 常見用途:同時需要鍵與值時的遍歷、
Map轉換、物件排序等。
const product = {
id: "A001",
name: "筆記型電腦",
price: 29999
};
const entries = Object.entries(product);
console.log(entries);
// [["id","A001"], ["name","筆記型電腦"], ["price",29999]]
// 轉成 Map 物件
const productMap = new Map(entries);
console.log(productMap.get("price")); // 29999
4. 為什麼要使用這三個方法?
| 情境 | 手寫 for...in |
Object.keys/values/entries |
|---|---|---|
| 只要鍵 | for (let k in obj) { if (obj.hasOwnProperty(k)) … } |
Object.keys(obj).forEach(k => …) |
| 只要值 | 需要再取一次 obj[k] |
Object.values(obj).forEach(v => …) |
| 同時需要鍵和值 | 兩層取得 | Object.entries(obj).forEach(([k,v]) => …) |
| 可鏈式操作 | 不易 | 支援 map、filter、reduce 等陣列方法 |
使用這三個靜態方法,程式碼更簡潔、可讀性更高,且自動過濾掉原型鏈上的屬性,降低錯誤機率。
程式碼範例(實用 5 篇)
範例 1:把物件轉成查詢字串(Query String)
function toQueryString(obj) {
// Object.entries 讓我們同時取得鍵和值
return Object.entries(obj)
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
}
const params = {
search: "JavaScript 教學",
page: 2,
limit: 20
};
console.log(toQueryString(params));
// output: search=JavaScript%20%E6%95%99%E5%AD%B8&page=2&limit=20
重點:使用
Object.entries可以一次取得鍵和值,配合Array.prototype.map完成資料映射。
範例 2:過濾出物件中「真值」的屬性
function filterTruthy(obj) {
// 先拿到鍵,再過濾值為 falsy 的項目
return Object.keys(obj)
.filter(key => Boolean(obj[key]))
.reduce((acc, key) => {
acc[key] = obj[key];
return acc;
}, {});
}
const raw = {
name: "Bob",
age: 0, // falsy, 會被過濾掉
email: "", // falsy
active: true
};
console.log(filterTruthy(raw)); // { name: "Bob", active: true }
說明:
Object.keys搭配filter與reduce,可以快速產生「淨化」後的物件。
範例 3:將物件的值平方後回傳新物件
function squareValues(obj) {
// 使用 Object.entries 直接取得鍵和值,map 後再轉回物件
const squared = Object.entries(obj).map(
([k, v]) => [k, typeof v === "number" ? v * v : v]
);
return Object.fromEntries(squared);
}
const numbers = { a: 2, b: 5, c: "hello", d: 10 };
console.log(squareValues(numbers));
// { a: 4, b: 25, c: "hello", d: 100 }
技巧:
Object.fromEntries(ES2019)可以把[[k,v], …]直接變回物件,寫法非常簡潔。
範例 4:依照物件值排序(從大到小)
function sortByValueDesc(obj) {
// 先取得 entries,依值排序,再轉回物件
const sorted = Object.entries(obj)
.sort(([, v1], [, v2]) => v2 - v1); // v2 - v1 => 降序
return Object.fromEntries(sorted);
}
const scores = { alice: 88, bob: 95, carol: 72 };
console.log(sortByValueDesc(scores));
// { bob: 95, alice: 88, carol: 72 }
應用:排行榜、統計圖表的資料前處理。
範例 5:深層遍歷物件的所有鍵(遞迴版)
function deepKeys(obj, prefix = '') {
let result = [];
for (const [key, value] of Object.entries(obj)) {
const fullKey = prefix ? `${prefix}.${key}` : key;
result.push(fullKey);
if (value && typeof value === 'object' && !Array.isArray(value)) {
// 只對純物件遞迴
result = result.concat(deepKeys(value, fullKey));
}
}
return result;
}
const nested = {
user: {
name: "Eve",
address: {
city: "Taipei",
zip: "106"
}
},
active: true
};
console.log(deepKeys(nested));
// [
// "user", "user.name", "user.address",
// "user.address.city", "user.address.zip", "active"
// ]
說明:即使是多層結構,仍能利用
Object.entries取得鍵和值,再遞迴產生「完整路徑」的鍵名。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 原型鏈屬性被遍歷 | 使用 for...in 時會列出原型上的屬性 |
改用 Object.keys/values/entries,或在 for...in 中加 obj.hasOwnProperty(key) |
Symbol 鍵不會被列出 |
三個方法只處理字串鍵 | 若需包含 Symbol,使用 Object.getOwnPropertySymbols 再自行合併 |
| 排序會改變原始物件順序 | 物件本身是無序集合,排序後必須重新產生新物件 | 使用 Object.entries → sort → Object.fromEntries,避免直接改變原物件 |
Object.values 回傳的陣列是 「引用」 還是「值」 |
若值是物件,陣列中的元素仍指向同一個參考 | 若需要深拷貝,使用 JSON.parse(JSON.stringify(...)) 或 structuredClone |
| 使用舊版瀏覽器不支援 | Object.entries、Object.fromEntries 在 IE 不支援 |
透過 Babel/polyfill 或改用 Object.keys + reduce 的方式實作 |
最佳實踐
- 盡量使用
Object.entries:同時需要鍵和值時,它是最直觀的選擇。 - 結合陣列方法:
map、filter、reduce能讓資料轉換一次搞定,避免多層迴圈。 - 保持不可變性:在函式式編程風格下,使用
Object.fromEntries產生新物件,避免直接變更原始資料。 - 考慮
enumerable屬性:若屬性被設定為enumerable: false,上述三個方法不會列出,必要時使用Object.getOwnPropertyNames。 - 使用 TypeScript 時加上型別:
Object.entries<T>(obj)能保留鍵值型別,提升編譯期安全。
實際應用場景
表單資料序列化
前端送出表單時,常把物件轉成 URL query string。Object.entries搭配map能快速完成編碼與串接。API 回傳資料的過濾與映射
從後端取得的 JSON 可能包含許多不必要欄位,使用Object.keys或Object.entries可以只保留需要的屬性,減少前端的資料負擔。動態產生 UI 列表
例如根據權限物件 ({ read: true, write: false, admin: true }) 產生對應的功能按鈕。Object.entries可直接遍歷鍵和值,判斷是否顯示。資料統計與報表
Object.values讓我們能輕鬆計算總和、平均、最大最小值,常用於儀表板或圖表的前處理。資料結構轉換
把普通物件轉成Map、Set、WeakMap等結構,或相反方向的轉換,都離不開Object.entries。
總結
Object.keys、Object.values、Object.entries是 遍歷與操作物件的核心工具,它們把「鍵」與「值」抽象成陣列,使我們能利用完整的陣列 API 進行映射、過濾、排序與聚合。- 使用這三個方法可以避免
for...in帶來的原型鏈問題、手動檢查hasOwnProperty的繁瑣,讓程式碼更簡潔、可讀且安全。 - 結合
Object.fromEntries、Array.prototype方法以及 ES6+ 的解構賦值,我們可以在 保持不可變性的同時 完成複雜的資料轉換與處理。 - 在實務開發中,從表單序列化、API 回傳資料的淨化、動態 UI 產生,到統計分析與結構轉換,這三個方法都是不可或缺的好幫手。
掌握 Object.keys / values / entries,就等於掌握了 物件資料的全方位操作能力,在日常開發與大型專案中,都能為你節省大量時間與降低錯誤率。祝你寫程式愉快,持續探索 JavaScript 的無限可能!