# 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.

```javascript
// 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

```javascript
// 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

```javascript
// 內建的 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

```javascript
// 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

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

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

## Object.getOwnPropertyDescriptors

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

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mistborn.gitbook.io/til-coding/javascript/property-flags-and-descriptors.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
