JavaScriptのプロパティディスクリプタは、プロパティ1つ1つに対して変更や列挙、削除の可否を指定できる仕組みである。仕様では、プロパティの属性("attribute")と表現されている。

わかりやすく言えば、プロパティの設定を行うオブジェクトだ。

種類

プロパティディスクリプタには、データプロパティとアクセサプロパティの2種類がある。

指定できるのはいずれか一方のみで、両方指定しようとすると型エラーが投げられる。注意したい。

データプロパティ

データプロパティは、プロパティの値に関する設定を担う。以下の4つのプロパティが該当する。プロパティという語が連発しててわかりづらい。データプロパティはディスクリプタのプロパティで、単純なプロパティはそのディスクリプタの設定対象を指す。

プロパティ 概要
[[Value]] プロパティの値。
[[Writable]] プロパティの値を変えられるかどうかを指定するフラグ。
[[Configurable]] プロパティの削除や属性(ディスクリプタの内容)を変えられるかどうかを指定するフラグ。[[Writable]]trueの場合、このフラグがfalseでもプロパティの値を変更できる。
[[Enumerable]] プロパティをfor...inあるいはObject.keys()を使ったループでイテレートできるかどうかを指定するフラグ。

アクセサディスクリプタ

アクセサディスクリプタは、要はclass構文でお馴染みのゲッターとセッターである。プロパティが参照されたり、代入されたりする際に実行する関数オブジェクトを指定する。

プロパティ 概要
[[Get]] プロパティが参照される際に実行する関数オブジェクトを指定する。
[[Set]] プロパティに値が代入される際に実行する関数オブジェクトを指定する。
[[Configurable]] 同じなので割愛。
[[Enumerable]] 同じなので割愛。

設定方法

プロパティディスクリプタの設定は、Object.defineProperty()メソッドあるいはObject.defineProperties()メソッドを使って行う。以下、データプロパティのフラグをぜんぶfalseにした例である。

js
const obj = {};
// 第一引数に対象オブジェクト、第二引数にプロパティキー、
// 第三引数にディスクリプタを指定する
Object.defineProperty(obj, "prop1", {
  value: 0,
  writable: false,
  configurable: false,
  enumerable: false,
});

obj.prop = 1;
for (let k in obj) {
  console.log(`${k}`);
}
delete obj.prop;

console.dir(obj);

// 結果:
// {prop: 0}

1を代入してもpropの値が変化せず、またfor...inは回らず、deleteでも削除されていないことがわかる。

なお、strictモードでは、書き込もうとしたり、削除しようとしたタイミングで型エラーが出て処理が停止する。

取得方法

プロパティのディスクリプタを取得したい場合には、Object.defineProperty()メソッドあるいはObject.getOwnPropertyDescriptor()を使う。

js
const obj = {};
Object.defineProperties(obj, {
  prop1: {
    value: 0,
    writable: false,
    configurable: false,
    enumerable: false,
  },
  prop2: {
    value: 1,
    writable: true,
    configurable: true,
    enumerable: true,
  },
});
// getOwnPropertyDescriptorはオブジェクトと取得したいプロパティキーを渡す
console.table(Object.getOwnPropertyDescriptor(obj, "prop1"));
// 結果:
// 0	false	false	false

// getOwnPropertyDescriptorsは単純にオブジェクトを渡す
console.table(Object.getOwnPropertyDescriptors(obj));
// 結果:
// prop1	0	false	false	false
// prop2	1	true	true	true

参考資料