目次
本書をサポートしてください。購入(PDF、EPUB、MOBI)または寄付
(広告、ブロックしないでください)

9. Object.getOwnPropertyDescriptors()

この章では、「Object.getOwnPropertyDescriptors()」というJordan HarbandとAndrea GiammarchiによるECMAScript 2017の機能について説明します。

9.1 概要

Object.getOwnPropertyDescriptors(obj)は、配列として、objのすべての所有プロパティのプロパティ記述子を返します。

const obj = {
    [Symbol('foo')]: 123,
    get bar() { return 'abc' },
};
console.log(Object.getOwnPropertyDescriptors(obj));

// Output:
// { [Symbol('foo')]:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

9.2 Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors(obj)はオブジェクトobjを受け取り、オブジェクトresultを返します。

プロパティ記述子は、プロパティの属性(値、書き込み可能かどうかなど)を記述します。詳しくは「Exploring JavaScript」の「プロパティ属性とプロパティ記述子」セクションを参照してください。

Object.getOwnPropertyDescriptors()を使う例です。

const obj = {
    [Symbol('foo')]: 123,
    get bar() { return 'abc' },
};
console.log(Object.getOwnPropertyDescriptors(obj));

// Output:
// { [Symbol('foo')]:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

Object.getOwnPropertyDescriptors()を実装する方法は次のとおりです。

function getOwnPropertyDescriptors(obj) {
    const result = {};
    for (let key of Reflect.ownKeys(obj)) {
        result[key] = Object.getOwnPropertyDescriptor(obj, key);
    }
    return result;
}

9.3 Object.getOwnPropertyDescriptors()のユースケース

9.3.1 ユースケース: プロパティをオブジェクトにコピーする

ES6以降、JavaScriptにはすでにプロパティをコピーするためのツールメソッドObject.assign()があります。ただし、このメソッドは単純なgetとsetの操作を使用して、キーがkeyのプロパティをコピーします。

const value = source[key]; // get
target[key] = value; // set

それは、デフォルト以外の属性(ゲッター、セッター、書き込み不可のプロパティなど)を持つプロパティを正しくコピーしないことを意味します。次の例はこの制限を説明しています。オブジェクトsourceにはキーがfooのセッターがあります。

const source = {
    set foo(value) {
        console.log(value);
    }
};
console.log(Object.getOwnPropertyDescriptor(source, 'foo'));
// { get: undefined,
//   set: [Function: foo],
//   enumerable: true,
//   configurable: true }

Object.assign()を使用してプロパティfooをオブジェクトtargetにコピーしようとすると失敗します。

const target1 = {};
Object.assign(target1, source);
console.log(Object.getOwnPropertyDescriptor(target1, 'foo'));
// { value: undefined,
//   writable: true,
//   enumerable: true,
//   configurable: true }

幸い、Object.getOwnPropertyDescriptors()Object.defineProperties()を一緒に使用すると機能します。

const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
console.log(Object.getOwnPropertyDescriptor(target2, 'foo'));
// { get: undefined,
//   set: [Function: foo],
//   enumerable: true,
//   configurable: true }

9.3.2 ユースケース: オブジェクトを複製する

シャロー複製はプロパティのコピーに似ているため、Object.getOwnPropertyDescriptors()もまた、ここでも適した選択肢です。

今回は、2つのパラメータを持つObject.create()を使用します。

const clone = Object.create(Object.getPrototypeOf(obj),
    Object.getOwnPropertyDescriptors(obj));

9.3.3 ユースケース: 任意のプロトタイプを持つクロスプラットフォームオブジェクトリテラル

任意のプロトタイプprotを持つオブジェクトを作成するためのオブジェクトリテラルを使用する構文的に最も優れた方法は、特殊プロパティ__proto__を使用することです。

const obj = {
    __proto__: prot,
    foo: 123,
};

残念なことに、その機能はブラウザでのみ保証されています。一般的な回避策はObject.create()と代入です。

const obj = Object.create(prot);
obj.foo = 123;

また、Object.getOwnPropertyDescriptors()を使用することもできます。

const obj = Object.create(
    prot,
    Object.getOwnPropertyDescriptors({
        foo: 123,
    })
);

もう1つの選択肢はObject.assign()です。

const obj = Object.assign(
    Object.create(prot),
    {
        foo: 123,
    }
);

9.4 落とし穴: superを使用するメソッドのコピー

superを使用する方法は、そのホームオブジェクト(それが格納されているオブジェクト)に密接に結びつけられます。そのようなメソッドを別のオブジェクトにコピーまたは移動する方法はありません。

次へ: 10. 関数のパラメータリストと呼び出しにおける後ろのコンマ