プロパティの詳細な指定

《 初回公開:2022/11/17 , 最終更新:未 》

【 目次 】

Object.definePropertyメソッドやObject.definePropertiesメソッドを使うとコードの記述は煩雑になるが、より詳細なプロパティの指定が可能。
Object.definePropertiesメソッドは複数のプロパティを設定指定。

以下のゲッターとセッターを操作するメソッドは現在では非推奨で、替わりにObject.definePropertyメソッドやObject.definePropertiesメソッドを使う事になる。

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メソッドは、オブジェクトの特定のプロパティのプロパティ記述子を返す。 それに対して、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
ページのトップへ戻る