本章将介绍由 Jordan Harband 和 Andrea Giammarchi 提供的ECMAScript提案——「Object.getOwnPropertyDescriptors()」
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 } }Object.getOwnPropertyDescriptors(obj)接受一个对象obj,然后返回一个对象:
遍历obj的属性(非继承的),然后在结果集中返回其相同的key和属性描述符对应的value。
属性描述符就是对属性特性的说明(其值,是否可写,等)。有关更多信息,请参阅“Speaking JavaScript” 相关章节 “Property Attributes and Property Descriptors”。
这是使用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;
}自ES6以来,JavaScript已有一个复制属性的工具方法:Object.assign()。 但是,此方法使用简单的get和set操作来复制键为所求键的属性:
const value = source[key]; // get
target[key] = value; // set这意味着它不能正确地复制带有非默认属性的属性(getters, setters, 不可写描述符, 等)。 下面的例子举例说明了此限制。这个对象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到一个目标对象就会失败:
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 }浅拷贝跟复制属性有点类似,这也是为什么Object.getOwnPropertyDescriptors()同样适用于这个用例。
这次,我们用Object.create(),它有两个参数:
第一个参数用来制定需要返回的对象属性。
可选的第二个参数是一个属性描述符集合,类似于Object.getOwnPropertyDescriptors()返回的集合。
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));如果你要给对象常量创建带任意原型的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,
}
);使用super的方法与其父对象(存储在其中的对象)紧密地连接着。 目前没有办法将这样的方法复制或移动到不同的对象。
下一章: 在函数参数列表和调用时后缀逗号