目录
请支持本书:购买 (PDF, EPUB, MOBI)捐赠
(广告,请勿屏蔽。)

9. Object.getOwnPropertyDescriptors()

本章解释了由 Jordan Harband 和 Andrea Giammarchi 提出的 ECMAScript 2017 特性“Object.getOwnPropertyDescriptors()”。

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

这意味着它无法正确复制具有非默认属性(getter、setter、不可写属性等)的属性。以下示例说明了此限制。对象 source 有一个键为 foo 的 setter。

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. 函数参数列表和调用中的尾随逗号