目錄
請支持這本書:購買它(PDF、EPUB、MOBI)捐款
(廣告,請不要阻擋。)

9. Object.getOwnPropertyDescriptors()

本章節說明 ECMAScript 2017 功能「Object.getOwnPropertyDescriptors()」,由 Jordan Harband 和 Andrea Giammarchi 提出。

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

屬性描述符描述屬性的屬性(其值、是否可寫入等)。更多資訊,請參閱「Speaking 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()。然而,此方法使用簡單的取得和設定操作來複製金鑰為 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() 在此也是一個好選擇。

這次,我們使用具有兩個參數的 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,
    })
);

另一個替代方式是 Object.assign()

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

9.4 陷阱:複製使用 super 的方法

使用 super 的方法與其家物件(儲存它的物件)緊密連結。目前沒有辦法複製或移動此類方法到不同的物件。

下一篇:10. 函式參數清單和呼叫中的尾隨逗號