はじめに
JavaScriptのプロパティ定義には以下のような方法があります。
const o1 = {
prop: 'hoge',
propFn: () => 10000,
};
const o2 = new Object();
o2.prop = 'hoge';
o2['propFn'] = () => 10000;
そしてこれら定義されたメンバはfor...inによって列挙することができます。
for (const property in o1) {
console.log(`${property}: ${o1[property]}`);
}
// expected output:
// prop: hoge
// propFn: () => 10000
for (const property in o2) {
console.log(`${property}: ${o2[property]}`);
}
// expected output:
// prop: hoge
// propFn: () => 10000
ふー! これでObjectのプロパティの定義方法と一覧を取得できることがわかりました! それでは。
……とはいかないですよね。そもそもここはまだ「はじめに」です。
for...inにはのっけから気になる記述があります。
for...in 文は、キーが文字列であるオブジェクトの列挙可能プロパティすべてに対して、継承された列挙可能プロパティも含めて反復処理を行います (Symbol がキーになったものは無視します)
オブジェクトの 列挙可能 プロパティすべて???
列挙可能・列挙不可能
『列挙可能プロパティ』 はリンクになっているため、リンク先を見てみます。するとプロパティの列挙可能性と所有権にたどり着きます。
検出の項目にはObject.prototype.propertyIsEnumerable()があるためとりあえず見てみましょう。
このページに書かれていることを引用すると以下の通りです。
- コード
obj.propertyIsEnumerable(
/** 調べたいプロパティの名前 */
) => /** 指定されたプロパティが列挙可能であり、かつオブジェクト自身のプロパティであるかどうか */
- 解説
すべてのオブジェクトは
propertyIsEnumerable
メソッドを持っています。このメソッドはあるオブジェクトのプロパティが、プロトタイプチェーンを通じて継承されたプロパティを除いてfor...in
ループで列挙可能かどうかを特定することができます。もしオブジェクトが指定されたプロパティを持っていない場合、このメソッドはfalse
を返します。
なるほど。 列挙されない プロパティを定義できるんですね。これは驚いた。
でも確かに、Objectにはパッと思いつくだけでもObject.prototype.toString()やObject.prototype.constructor
が生えているのに列挙されていませんね。気づかなかっただけでずっとそこにいたわけです。
でもどうやって定義するのでしょうか?
列挙不可能なプロパティを定義
元のページに戻ると以下の記述があります。
Object.defineProperty
で追加したプロパティはデフォルトで列挙可能性がfalse
になります
Object.defineProperty()でもプロパティを追加できるのか。
見てみましょう。使い方は以下の通り。
/**
* @param obj プロパティを定義するオブジェクト
* @param prop 定義または変更するプロパティの名前またはSymbol
* @param descriptor 定義または変更するプロパティの記述子
* @return この関数に渡されたオブジェクト
*/
Object.defineProperty(obj, prop, descriptor)
obj
は対象のオブジェクト、 prop
はキーに該当するもので間違い無いでしょう。では descriptor
は?
それは解説に書かれています。
そのまま引用します。
configurable
true
である場合のみ、この種の記述子を変更することや、対応するオブジェクトからプロパティを削除することができます。 既定値はfalse
です。
enumerable
true
である場合のみ、このプロパティは対応するオブジェクトでのプロパティ列挙に現れます。 既定値はfalse
です。
データ記述子は以下のオプションキーも持ちます。
value
プロパティに関連づけられた値です。有効なJavaScriptの値(number
, object
, function
など) である必要があります。
既定値はundefined
です。
writable
true
である場合のみ、プロパティに関連づけられた値は代入演算子で変更することができます。既定値はfalse
です。
なるほどなるほど。列挙する・しないはenumerable
で決めることができるんですね。
また加えてdeleteできるかどうかや書き換えることができるかどうかなども設定できるとは。いやはや恐れ入りました。
確かに書き換えたり削除したりすることができない値もありますもんね。
delete document.location // false
参考: delete演算子
オブジェクトのプロパティ定義方法についてを修めるつもりがプロパティの種類までわかってしまいました。棚ボタです!
ちなみにですが、このdescriptor
ですが、TypeScriptのDecoratorの文脈でも出てきます。(参考)
おわりに
検出の項目にはObject.prototype.propertyIsEnumerable()があるためとりあえず見てみましょう。
の部分にやや無理があることは承知していますが、持っていきたい方向があったため流れを優先しました。