JavaScript Object.keys() や Object.entries() でキーや値が取得できない
JavaScript の Object.defineProperty()
で定義したプロパティで Object.keys()
や Object.entries()
でキーや値が取得できない場合の対応方法です。
やりたいこと
以下のように Object.defineProperty()
でプロパティと値が定義されたオブジェクトがあり、
const obj = {};
Object.defineProperty(obj, "key1", { value: "value1" });
以下のようにオブジェクトのプロパティ名の一覧を取得したかったのですが、空配列が返ってきてしまいました。
Object.keys(obj); // []
以下のように Object.entries()
も試しましたが空配列でした。
Object.entries(obj); // []
Object.keys()
は列挙可能なプロパティの配列を返す
MDN で Object.keys()
の説明をちゃんと読んでみると以下の記載がありました。
Object.keys()
は、object
で直接発見された列挙可能なプロパティに対応する文字列を要素とする配列を返します。 - Object.keys() - JavaScript | MDN
列挙可能?
なお、 Object.entries()
にも同様の記載があります。
Object.entries()
は、object
に直接存在する文字列をキーとした列挙可能プロパティの組[key, value]
が配列要素に対応した配列を返します。 - Object.entries() - JavaScript | MDN
列挙可能とは
さて、先ほどのオブジェクトの定義を以下のように変更してみます。
const obj = {};
obj.key1 = "value1";
普通はこのように定義することが多いと思いますが、この場合は Object.keys()
や Object.entries()
は期待通りの挙動をします。
Object.keys(obj) // ["key1"]
Object.entries(obj) // [["key1", "value1"]]
ということは Object.defineProperty()
に問題がありそうです。
改めて MDN で Object.defineProperty()
を確認してみます。
Object.defineProperty(obj, prop, descriptor);
obj
- プロパティを定義するオブジェクトです。
prop
- 定義または変更するプロパティの名前または
Symbol
です。descriptor
- 定義または変更するプロパティの記述子です。 - Object.defineProperty() - JavaScript | MDN
プロパティの記述子とは?と思い読み進めてみると…。ありました!
enumerable
true
である場合のみ、このプロパティは対応するオブジェクトでのプロパティ列挙に現れます。 既定値はfalse
です。 - Object.defineProperty() - JavaScript | MDN
enumerable
をプロパティの記述子内で指定していなかったので、デフォルトの false
になり、列挙可能ではないプロパティになり、Object.keys()
や Object.entries()
の結果に現れなかったようです。
Object.defineProperty()
で列挙可能なプロパティを定義する
ということで、プロパティの記述子の enumerable
を true
にしてプロパティを定義したところ、Object.keys()
や Object.entries()
でキーや値が取得できるようになりました。
const obj = {};
Object.defineProperty(obj, "key1", { value: "value1", enumerable: true });
Object.keys(obj) // ["key1"]
Object.entries(obj) // [["key1", "value1"]]
なお、Object.getOwnPropertyNames()
を使用すれば、プロパティの記述子の enumerable
によらず、全プロパティー名を配列で取得することが可能です。
const obj = {};
Object.defineProperty(obj, "key1", { value: "value1" });
Object.getOwnPropertyNames(obj) // ["key1"]
Object.keys()
は列挙可能なプロパティに限定できるので、厳密なオブジェクトを定義したければ、Object.defineProperty()
のプロパティの記述子でコントロールし、Object.keys()
で列挙するほうが小回りは効きそうです。
ちなみに JavaScript のビルトインオブジェクトはプロパティの記述子で enumerable
が false
になっており、列挙可能ではないプロパティがほとんどですが、そのプロパティの記述子で configurable
が true
であれば、Object.defineProperty()
で列挙可能に変更することができます。
const err = new Error("Invalid.");
Object.keys(err); // []
// プロパティの記述子を確認
Object.getOwnPropertyDescriptor(err, "message");
/**
* {
* value: "Invalid.",
* writable: true,
* enumerable: false,
* configurable: true
* }
*/
// enumerable を true に変更
Object.defineProperty(err, "message", { enumerable: true });
Object.keys(err); // ["message"]
あとがき
わざわざ Object.defineProperty()
でプロパティ設定とかあまり需要なさそうですが、列挙可能とか知らなかったのでメモとして残してみました。