プロパティの詳細な指定
《 初回公開:2022/11/17 , 最終更新:未 》
【 目次 】
Object.definePropertyメソッドやObject.definePropertiesメソッドを使うとコードの記述は煩雑になるが、より詳細なプロパティの指定が可能。
Object.definePropertiesメソッドは複数のプロパティを設定指定。
以下のゲッターとセッターを操作するメソッドは現在では非推奨で、替わりにObject.definePropertyメソッドやObject.definePropertiesメソッドを使う事になる。
- Object.prototype.defineSetter() - JavaScript | MDN
- Object.prototype.defineGetter() - JavaScript | MDN
definePropertyの構文は
Object.defineProperty(obj, prop, descriptor)
obj:プロパティを定義するオブジェクト
prop:プロパティ名
descriptor引数:連想配列(オブジェクト)を使ってプロパティ記述子によりプロパティの属性をキーとその値で指定
プロパティ記述子
descriptorはプロパティ記述子と呼ばれ次の2種類の記述子がある。
- データ記述子
- 値を持つプロパティ(メソッドも含む,ゲッターとセッター以外)
- アクセサー記述子
- ゲッターとセッターのみ
データ記述子とアクセサー記述子で共通に指定できるオプションキー
- configurable
- trueならプロパティの定義を変更, 削除する事が可能、既定値は false
- enumerable
- trueなら、for…in loopやObject.keysメソッド等を使ってプロパティの列挙可能。 既定値は false
データ記述子のオプションキー
- value
- プロパティに関連づけられた値です。有効な JavaScript の値 (number, object, function など) である必要があります。 既定値は undefined です。
- writable
- true なら、プロパティの値を書き込み可能。既定値は false
アクセサー記述子のオプションキー
- get
- ゲッター関数を指定
- set
- セッター関数を指定
データ記述子の例
オブジェクトリテラル表記によるプロパティの定義は
let o = { a: "aaa", n: 99 };
console.log(o); //{a: 'aaa', n: 99}
これと同じ事をObject.definePropertyメソッドで記述すると
let o = {};
Object.defineProperty(o, "a",
{ value: "aaa", configurable: true, enumerable: true, writable: true });
Object.defineProperty(o, "n",
{ value: 99, configurable: true, enumerable: true, writable: true });
console.log(o); //{a: 'aaa', n: 99}
configurable属性、enumerable属性、writable属性の指定は省略可能だが、
オブジェクトリテラル表記やドット表記でプロパティを設定するとconfigurable属性、enumerable属性、writable属性はデフォルトではtrue。
ところが、Object.definePropertyメソッドではfalse。
従って、同じにするには明示的にconfigurable属性、enumerable属性、writable属性をtrueに設定する必要がある。
definePropertiesメソッドでaとnの複数のプロパティを一度に記述すると。
let o = {};
Object.defineProperties(o, {
a: { value: "aaa", configurable: true, enumerable: true, writable: true },
n: { value: 99, configurable: true, enumerable: true, writable: true }
});
console.log(o); //{a: 'aaa', n: 99}
アクセサー記述子の例
ゲッターとセッターはオブジェクトリテラル表記では記述できない
class宣言内で記述するか、Object.definePropertyメソッドやObject.definePropertiesメソッドで記述する必要がある。
Object.definePropertyメソッドで記述すると
一般的なゲッターとセッターのコードは、
まずオブジェクトを生成してゲッターとセッターからアクセスする内部変数_xを定義。
let o = new Object();
Object.defineProperty(o, "_x",
{ value: "プロパティの初期値", configurable: true, enumerable: false, writable: true });
いよいよゲッターとセッターを定義。
Object.defineProperty(o, "x",
{
set: function (x) {
this._x = x;
},
get: function () {
return this._x;
},
configurable: true, enumerable: true
});
o.x = "xxx";
console.log(o.x); // xxx
ゲッターとセッターは簡略表記
ゲッターとセッターは簡略表記が可能でこれをメソッド名ショートハンドと呼ぶようだ。
Object.defineProperty(o, "x",
{
set(x) {
this._x = x;
},
get() {
return this._x;
},
configurable: true, enumerable: true
});
o.x = "xxx";
console.log(o.x); // xxx
これを内部変数_xも含めてObject.definePropertiesメソッドで記述すると
let o = new Object();
Object.defineProperties(o, {
_x: { value: "プロパティの初期値", configurable: true, enumerable: true, writable: true },
x: {
set(x) {
this._x = x;
},
get() {
return this._x;
},
configurable: true, enumerable: true
}
});
o.x = "xxx";
console.log(o.x); // xxx
プロパティの上書き
ゲッターとセッターはconfigurable属性がtrueなら上書き可能
Object.defineProperty(o, "x",
{
set(x) {
this._x = "*" + x + "*";
},
configurable: true, enumerable: true
});
o.x = "xxx";
console.log(o.x); // *xxx*
データ記述子の場合はconfigurable属性がtrueでwritable属性がtrueなら上書き可能で、
writable属性をfalseにしてプロパティの値を上書きしても値は変化しない。
Object.defineProperty(o, "a",
{ value: "aaa", configurable: true, enumerable: true, writable: false });
o.a = "bbb"
console.log(o.a); // aaa
プロパティ記述子の取得
Object.getOwnPropertyDescriptorメソッドやObject.getOwnPropertyDescriptorsメソッドを使うと既に定義済みのプロパティの記述子の値を確認できる。
- Object.getOwnPropertyDescriptor() - JavaScript | MDN
- Object.getOwnPropertyDescriptors() - JavaScript | MDN
Object.getOwnPropertyDescriptorメソッドは、オブジェクトの特定のプロパティのプロパティ記述子を返す。 それに対して、Object.getOwnPropertyDescriptorsメソッドは、指定したオブジェクトのすべてのプロパティ記述子を返す。
let o = { a: "aaa", n: 99 };
//Object.getOwnPropertyDescriptor
let d = Object.getOwnPropertyDescriptor(o, 'a');
for (const key in d) {
console.log(`${key}: ${d[key]}`)
}
// getOwnPropertyDescriptors
const ds = Object.getOwnPropertyDescriptors(o);
console.log(ds.a.writable);
console.log(ds.a.value);
console.log(ds.n.writable);
console.log(ds.n.value);
for (const d in ds) {
for (const key in ds[d]) {
console.log(`${d}.${key}: ${ds[d][key]}`)
}
}
実行結果
value: aaa writable: true enumerable: true configurable: true true aaa true 99 a.value: aaa a.writable: true a.enumerable: true a.configurable: true n.value: 99 n.writable: true n.enumerable: true n.configurable: true
definePropertyでメソッドの定義
オブジェクトリテラル表記でメソッドを定義すると
let o = {
method: function () {
console.log("methodの実行");
}
}
o.method(); // methodの実行
この時プロパティの記述子がどうなっているかというと
let d = Object.getOwnPropertyDescriptor(o, 'method');
for (const key in d) {
console.log(`${key}: ${d[key]}`)
}
実行結果
value: function () { console.log("methodの実行"); } writable: true enumerable: true configurable: true
value属性の値に関数が指定されている。
という事はメソッド定義は、データ記述子により属性を指定する事になり、
Object.definePropertyメソッドを使って。
Object.defineProperty(o, "method2",
{
value: function () {
console.log("method2の実行");
}, configurable: true, enumerable: true, writable: true
});
o.method2(); // method2の実行
ゲッターとセッターと同様にメソッド名ショートハンドも可能なようで
Object.defineProperty(o, "method3",
{
value() {
console.log("method3の実行");
}, configurable: true, enumerable: true, writable: true
});
o.method3(); // method3の実行
enumerable属性とObject.prototype.propertyIsEnumerableメソッド
MDNのドキュメントによると
enumerable プロパティ属性は、そのプロパティが Object.assign() や スプレッド演算子で採り上げられるかどうかを定義します。Symbol 以外のプロパティでは、 for...in ループや Object.keys() に現れるかどうかも定義します。
Object.prototype.propertyIsEnumerableメソッドを使って列挙可能(enumerable属性がtrue)かプロパティを確認できる。
propertyIsEnumerableメソッドはプロパティのenumerable属性を返す。
気をつけなければいけないのは指定したプロパティが存在しない場合もfalseを返す事。
let o = {};
Object.defineProperty(o, "a",
{ value: "aaa", configurable: true, enumerable: true, writable: true });
console.log(o.propertyIsEnumerable('a')); // true
Object.defineProperty(o, "b",
{ value: "bbb", configurable: true, enumerable: false, writable: true });
console.log(o.propertyIsEnumerable('b')); // false
配列について確認してみると
const array1 = [];
array1[0] = 42;
// 配列の要素は列挙可能だが
console.log(array1.propertyIsEnumerable(0)); // true
// lengthプロパティは列挙されては困るので
console.log(array1.propertyIsEnumerable('length')); // false