JavaScriptの"writable","enumerable","configurable"

# はじめに

JavaScriptのオブジェクトには各プロパティに属性があり、以下の関数で確認できます。

```javascript
const person = { name: "太郎" };
const descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.table(descriptor);
```
実行結果
![descriptor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/9c6016e7-512b-f06b-7e2b-211f2b0e2802.png)

本記事では3つのプロパティ属性について、まとめたいと思います。
* writable
* enumerable
* configurable

# writable（書込可能）  
対象のプロパティに値の書き込みが可能であるか or 不可であるかを示しています。  
trueの場合に書き込みが可能です。

プロパティ属性の値を変更するには ```Object.defineProperty()``` を使用します。

```javascript
const person = { name: "太郎" };
Object.defineProperty(person, "name", { writable: false });
const descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.table(descriptor);
```
![descriptor2png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/e1b684b4-5138-dcaa-4f98-eee52f053e16.png)

それでは実際に writable が false の場合に書き込みが不可であるか試してみます。

:::note warn
"use strict" の有無によって結果が異なるので注意
:::

## "use strict" なし

```javascript
const person = { name: "太郎" };
Object.defineProperty(person, "name", { writable: false });
person.name = "花子";
console.log(person.name);
// 太郎
```
最後まで実行されましたが、値の代入は行われませんでした。

## "use strict" あり

```javascript
"use strict";
const person = { name: "太郎" };
Object.defineProperty(person, "name", { writable: false });
person.name = "花子";
console.log(person.name);
// Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
```
最後まで実行されず、値を代入するタイミングでエラーとなりました。

# enumerable（列挙可能）  
```for..in``` や ```Object.keys()``` のようなプロパティを列挙する関数において、対象のプロパティを列挙するか or しないかを示しています。trueの場合にプロパティが列挙されます。

今回は ```Object.keys()```、```Object.values()```、```Object.entries()``` の関数で試してみます。

## enumerable が true の場合

```javascript
const person = { name: "太郎", sex: "男性", age: "30" };
console.log(Object.keys(person));
console.log(Object.values(person));
console.log(Object.entries(person));
```
![enumerable_true.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/271baeab-550e-8d8e-da81-c9e24a8c0d81.png)

正しく "name" プロパティが列挙されました。

## enumerable が false の場合

```javascript
const person = { name: "太郎", sex: "男性", age: "30" };
Object.defineProperty(person, "name", { enumerable: false });
console.log(Object.keys(person));
console.log(Object.values(person));
console.log(Object.entries(person));
```    

![enumerable_false.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/367cfd95-0425-155c-5386-538ba1e94a5d.png)

"name" プロパティが列挙の対象外となり、配列の長さが 2 となりました。

# configurable（構成可能）
このプロパティ属性がfalseとなった場合、以下の操作が不可となります。
* enumerable 属性の変更
* オブジェクトからプロパティの削除

それぞれ実際に試してみます。

## enumerable 属性の変更

```javascript
const person = { name: "太郎" };
Object.defineProperty(person, "name", { configurable: false });
Object.defineProperty(person, "name", { enumerable: false });
// Uncaught TypeError: Cannot redefine property: name at Function.defineProperty (<anonymous>)
```

再定義が不可であるとエラーが表示されました。

:::note info
ちなみに writable 属性の変更は可能です。
:::

## オブジェクトからプロパティの削除

まずは configurableがtrueの場合にプロパティが削除できるか確認します。

```javascript
const person = { name: "太郎", sex: "男性", age: 30 };
delete person.name;
console.log(person);
```

![configurable_true.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/927eb42b-c493-e4a3-909b-bcbd03f9e3d8.png)

それでは次に、configurableがfalseの場合を試してみます。

:::note warn
"use strict" の有無によって結果が異なるので注意
:::

### "use strict" なし

```javascript
const person = { name: "太郎", sex: "男性", age: 30 };
Object.defineProperty(person, "name", { configurable: false });
delete person.name;
console.log(person);
```

![configurable_false.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/e06828c4-30a3-b36a-410c-61fd820b2b79.png)

最後まで実行されましたが、"name" プロパティの削除は行われませんでした。

### "use strict" あり

```javascript
"use strict";
const person = { name: "太郎", sex: "男性", age: 30 };
Object.defineProperty(person, "name", { configurable: false });
delete person.name;
console.log(person);
// Uncaught TypeError: Cannot delete property 'name' of #<Object>
```

最後まで実行されず、削除しようとした時点でエラーとなりました。


# 参考サイト

https://javascript.keicode.com/lang/object-object.php