本文 AI 產出,尚未審核
JavaScript 物件(Objects)── 屬性操作(讀取、寫入、刪除)
簡介
在 JavaScript 中,物件是最常見、也是最彈性的資料結構。幾乎所有的程式碼都會與物件互動,從簡單的設定參數到複雜的資料模型,都離不開「屬性」的讀寫與刪除。掌握屬性操作的細節,不僅能寫出更易讀、可維護的程式,也能避免常見的 bug(例如意外覆寫、原型鏈污染等)。
本篇文章將以 讀取、寫入、刪除 為主軸,從語法到實務應用,層層剖析物件屬性的操作方式,並提供實用範例、常見陷阱與最佳實踐,幫助初學者建立扎實基礎,同時為中級開發者提供進階觀點。
核心概念
1. 物件屬性的基本結構
const person = {
name: "Alice", // key: name, value: "Alice"
age: 28,
isStudent: false
};
- key(屬性名稱):可以是字串或 Symbol。
- value(屬性值):任何合法的 JavaScript 值(原始值、函式、物件…)。
屬性在底層是 屬性描述子(property descriptor),包含 value、writable、enumerable、configurable 等屬性,稍後會提到。
2. 讀取屬性
2.1 點記法 vs 方括號記法
| 方式 | 語法 | 何時使用 |
|---|---|---|
| 點記法 | obj.prop |
屬性名稱是有效的 identifier(不含空格、特殊字元) |
| 方括號記法 | obj["prop"] 或 obj[expr] |
屬性名稱動態決定,或包含特殊字元、保留字時 |
// 點記法
console.log(person.name); // "Alice"
// 方括號記法(屬性名稱動態取得)
const key = "age";
console.log(person[key]); // 28
// 包含特殊字元的屬性只能用方括號
person["first-name"] = "Alice";
console.log(person["first-name"]); // "Alice"
2.2 讀取不存在的屬性
當屬性不存在時,會回傳 undefined,且不會拋出錯誤。
console.log(person.height); // undefined
小技巧:使用 可選鏈(optional chaining)
?.可以安全地讀取深層屬性,避免TypeError。
const city = person.address?.city; // 若 address 為 undefined,city 也會是 undefined
3. 寫入屬性
3.1 新增或覆寫
// 新增屬性
person.email = "alice@example.com";
// 覆寫屬性
person.age = 29;
3.2 使用 Object.defineProperty 控制屬性行為
Object.defineProperty(person, "id", {
value: 12345,
writable: false, // 不允許修改
enumerable: false, // 不會在 for...in 中出現
configurable: false // 之後無法刪除或重新定義
});
console.log(person.id); // 12345
person.id = 99999; // 靜默失敗(在嚴格模式下會拋錯)
3.3 合併多個屬性
Object.assign(person, {
gender: "female",
hobby: ["reading", "travel"]
});
console.log(person.gender); // "female"
4. 刪除屬性
4.1 delete 操作符
delete person.email; // 刪除成功,返回 true
console.log(person.email); // undefined
注意:
delete只能刪除 自有屬性(own property),且無法刪除configurable: false的屬性。
4.2 Reflect.deleteProperty(更安全的寫法)
const result = Reflect.deleteProperty(person, "age");
console.log(result); // true
Reflect.deleteProperty 的返回值永遠是布林值,讓程式更易於判斷刪除是否成功。
5. 其他實用操作
5.1 列舉屬性
for (const key in person) {
if (person.hasOwnProperty(key)) { // 排除原型鏈上的屬性
console.log(`${key}: ${person[key]}`);
}
}
5.2 取得屬性清單
const keys = Object.keys(person); // ["name","age","isStudent",...]
const values = Object.values(person); // ["Alice",28,false,...]
const entries = Object.entries(person); // [["name","Alice"],["age",28],...]
常見陷阱與最佳實踐
| 陷阱 | 說明 | 最佳實踐 |
|---|---|---|
| 意外覆寫原型屬性 | 直接寫入 obj.__proto__ 或 obj.constructor 可能改變原型鏈。 |
使用 Object.create(null) 建立 純粹物件,或避免使用 __proto__。 |
delete 效能問題 |
delete 會影令引擎重新整理物件的 hidden class,影響效能。 |
盡量使用 null/undefined 取代刪除,或在初始化時就不建立不必要的屬性。 |
| 屬性不可寫 | 使用 Object.defineProperty 時若 writable:false,後續寫入會失敗。 |
確認需求後才設定 writable:false,或在開發階段先使用 writable:true 以免卡住。 |
| 深層屬性讀取未檢查 | obj.a.b.c 若 a 為 undefined 會拋 TypeError。 |
使用 可選鏈 obj?.a?.b?.c,或事先檢查 if (obj && obj.a) …。 |
| 方括號記法的安全性 | obj[expr] 若 expr 為使用者輸入,可能導致意外覆寫重要屬性。 |
先 驗證 expr 是否在允許的屬性清單中。 |
實際應用場景
1. 表單資料的 動態收集
function collectFormData(form) {
const data = {};
const inputs = form.querySelectorAll("[name]");
inputs.forEach(input => {
const name = input.name;
// 使用方括號記法,因為 name 可能是動態產生的
data[name] = input.value;
});
return data;
}
2. 設定物件的唯讀屬性(防止外部修改)
function createUser(name, age) {
const user = {};
Object.defineProperty(user, "name", {
value: name,
writable: false,
enumerable: true,
configurable: false
});
user.age = age; // 一般屬性,可寫
return user;
}
const u = createUser("Bob", 35);
u.name = "Alice"; // 靜默失敗(嚴格模式下會拋錯)
3. 快取(Cache)機制:刪除過期項目
const cache = {};
function setCache(key, value, ttl) {
cache[key] = { value, expires: Date.now() + ttl };
}
function getCache(key) {
const entry = cache[key];
if (!entry) return undefined;
if (Date.now() > entry.expires) {
// 刪除過期資料
delete cache[key];
return undefined;
}
return entry.value;
}
4. 使用 Reflect 進行安全的屬性操作(如在 Proxy 中)
const handler = {
deleteProperty(target, prop) {
console.log(`Attempt to delete ${prop}`);
return Reflect.deleteProperty(target, prop);
}
};
const obj = new Proxy({ a: 1, b: 2 }, handler);
delete obj.a; // 觸發 Proxy 的 deleteProperty
總結
- 讀取屬性:點記法簡潔、方括號記法彈性;使用可選鏈避免深層讀取錯誤。
- 寫入屬性:直接賦值最常見,
Object.defineProperty可精細控制writable、enumerable、configurable。 - 刪除屬性:
delete與Reflect.deleteProperty皆可,注意configurable:false的限制與效能影響。 - 最佳實踐:盡量避免在執行效能關鍵路徑使用
delete,使用null/undefined代替;對於需要保護的屬性,使用Object.defineProperty設為唯讀;動態屬性名務必做好驗證。
掌握這三大操作,你就能在 JavaScript 中自如地管理物件資料,從簡單的設定值到複雜的資料模型,都能寫出 可讀、可維護且安全 的程式碼。祝你在物件的世界裡玩得開心,寫出更好、更可靠的 JavaScript 應用!