Property flags and descriptors

Property flags

屬性除了值還有 3 個標誌,創造屬性時不會看到他們,因為預設皆為 true。

  • writable – if true, can be changed, otherwise it’s read-only.

  • enumerable – if true, then listed in loops, otherwise not listed.

  • configurable – if true, the property can be deleted and these attributes can be modified, otherwise not.

// Object.getOwnPropertyDescriptor() 可以完整取得屬性的資訊
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);

let user = {
  name: "John"
};

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
  "value": "John",
  "writable": true,
  "enumerable": true,
  "configurable": true
}
*/

// Object.defineProperty() 可以改變屬性的標誌
Object.defineProperty(obj, propertyName, descriptor)

// 屬性存在會改變標誌,屬性不存在會創造,但標誌皆為 false
let user = {};

Object.defineProperty(user, "name", {
  value: "John"
});

let descriptor = Object.getOwnPropertyDescriptor(user, 'name');

alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
  "value": "John",
  "writable": false,
  "enumerable": false,
  "configurable": false
}
 */

Read-only

// writable: false 不可修改
let user = {
  name: "John"
};

Object.defineProperty(user, "name", {
  writable: false
});

user.name = "Pete"; // Error: Cannot assign to read only property 'name'...

// 對新增的屬性要列出標誌的值,不然皆為 false
let user = { };

Object.defineProperty(user, "name", {
  value: "Pete",
  // for new properties need to explicitly list what's true
  enumerable: true,
  configurable: true
});

alert(user.name); // Pete
user.name = "Alice"; // Error

Non-enumerable

// 內建的 toString 不會顯示在迴圈,但自定義的 toString 會出現在迴圈
let user = {
  name: "John",
  toString() {
    return this.name;
  }
};

// 默认情况下,我们的两个属性都会列出:
for (let key in user) alert(key); // name, toString

// enumerable: false toString 不會出現在迴圈
let user = {
  name: "John",
  toString() {
    return this.name;
  }
};

Object.defineProperty(user, "toString", {
  enumerable: false
});

// Now our toString disappears:
for (let key in user) alert(key); // name

// 也不會出現在 Object.keys()
alert(Object.keys(user)); // name

Non-configurable

// configurable: false 不能藉由 Object.defineProperty() 刪除或修改屬性
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');

alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
  "value": 3.141592653589793,
  "writable": false,
  "enumerable": false,
  "configurable": false
}
*/

// 不能刪除屬性
Math.PI = 3; // Error
// delete Math.PI won't work either

// configurable: false 沒有回頭路,會鎖住所有屬性資訊無法更改
let user = { };

Object.defineProperty(user, "name", {
  value: "John",
  writable: false,
  configurable: false
});

// won't be able to change user.name or its flags
// all this won't work:
//   user.name = "Pete"
//   delete user.name
//   defineProperty(user, "name", ...)
Object.defineProperty(user, "name", {writable: true}); // Error

Object.defineProperties

// syntax
Object.defineProperties(obj, {
  prop1: descriptor1,
  prop2: descriptor2
  // ...
});

// example
Object.defineProperties(user, {
  name: { value: "John", writable: false },
  surname: { value: "Smith", writable: false },
  // ...
});

Object.getOwnPropertyDescriptors

// 可以用迴圈複製物件
for (let key in user) {
  clone[key] = user[key]
}

// 用 Object.getOwnPropertyDescriptors() + Object.defineProperties() 式更好的方法
// 可以複製所有屬性的資訊
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));

Last updated