本文 AI 產出,尚未審核

JavaScript 物件(Objects)── 屬性操作(讀取、寫入、刪除)


簡介

在 JavaScript 中,物件是最常見、也是最彈性的資料結構。幾乎所有的程式碼都會與物件互動,從簡單的設定參數到複雜的資料模型,都離不開「屬性」的讀寫與刪除。掌握屬性操作的細節,不僅能寫出更易讀、可維護的程式,也能避免常見的 bug(例如意外覆寫、原型鏈污染等)。

本篇文章將以 讀取、寫入、刪除 為主軸,從語法到實務應用,層層剖析物件屬性的操作方式,並提供實用範例、常見陷阱與最佳實踐,幫助初學者建立扎實基礎,同時為中級開發者提供進階觀點。


核心概念

1. 物件屬性的基本結構

const person = {
  name: "Alice",   // key: name, value: "Alice"
  age: 28,
  isStudent: false
};
  • key(屬性名稱):可以是字串或 Symbol。
  • value(屬性值):任何合法的 JavaScript 值(原始值、函式、物件…)。

屬性在底層是 屬性描述子(property descriptor),包含 valuewritableenumerableconfigurable 等屬性,稍後會提到。


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.caundefined 會拋 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 可精細控制 writableenumerableconfigurable
  • 刪除屬性deleteReflect.deleteProperty 皆可,注意 configurable:false 的限制與效能影響。
  • 最佳實踐:盡量避免在執行效能關鍵路徑使用 delete,使用 null/undefined 代替;對於需要保護的屬性,使用 Object.defineProperty 設為唯讀;動態屬性名務必做好驗證。

掌握這三大操作,你就能在 JavaScript 中自如地管理物件資料,從簡單的設定值到複雜的資料模型,都能寫出 可讀、可維護且安全 的程式碼。祝你在物件的世界裡玩得開心,寫出更好、更可靠的 JavaScript 應用!