Skip to main content

Object.getOwnPropertyDescriptors()

语法

getOwnPropertyDescriptors<T>(o: T): {[P in keyof T]: TypedPropertyDescriptor<T[P]>} & { [x: string]: PropertyDescriptor };

描述

用来获取一个对象所有自有属性的描述符,如果没有任何自身属性,则返回空对象。

示例

下面的代码创建一个 obj 对象,该对象有三个属性 firstName, lastName, age 和一个方法 greeting. 其中 lastName 被重定义成访问器属性,age 属性不可写、不可配置、不可枚举。

const obj = {
firstName: 'Yancey',
lastName: 'Leo',
greeting() {
return `Hello, ${this.firstName}!`
},
}

Object.defineProperties(obj, {
age: {
value: 18,
enumerable: false,
},
lastName: {
get() {
return 'Yancey'
},
},
})

obj.__proto__ = {
somePrototypeKey: 'somrthing...',
}

通过 Object.getOwnPropertyDescriptors() 可获取一个对象所有自有属性的描述符,但不会输出原型上的属性。

TIP

观察下面代码,我们很容易看出 Object.getOwnPropertyDescriptors() 的返回值其实就是 Object.defineProperties() 的 第二个参数

Object.getOwnPropertyDescriptors(obj);

// 输出如下:
{
firstName: {
value: 'Yancey',
writable: true,
enumerable: true,
configurable: true
},
lastName: {
get: [Function: get],
set: undefined,
enumerable: true,
configurable: true
},
greeting: {
value: [Function: greeting],
writable: true,
enumerable: true,
configurable: true
},
age: {
value: 18,
writable: false,
enumerable: false,
configurable: false
}
}

如果没有任何自身属性,则返回空对象。

Object.getOwnPropertyDescriptors({}) // {}

扩展

解决 Object.assign() 浅拷贝问题

Object.assign() 浅拷贝一个对象时存在以下问题:

  • 只能拷贝源对象的可枚举的自身属性

  • 无法拷贝属性的特性们

  • 访问器属性会被转换成数据属性

  • 无法拷贝源对象的原型

我们仍然使用 obj 对象,并使用 Object.assign() 克隆出一个新的对象。

const newObj = Object.assign({}, obj);

Object.getOwnPropertyDescriptors(newObj);
// 输出:
{
firstName: {
value: 'Yancey',
writable: true,
enumerable: true,
configurable: true
},
// lastName 从访问器属性变成了数据属性
lastName: {
value: 'Yancey',
writable: true,
enumerable: true,
configurable: true
},
greeting: {
value: [Function: greeting],
writable: true,
enumerable: true,
configurable: true
}
// age因为不可枚举,所以没有被拷贝
}

// 原型也没有被拷贝
newObj.__proto__; // {}

所以我们可以使用 Object.getOwnPropertyDescriptors() 配合 Object.create() 和 Object.getPropertyOf() 来解决这个问题。

Object.create() 接收两个参数:

  • 第一个是新创建对象的原型对象,这里我们通过 Object.getPrototypeOf(obj) 获取

  • 第二个参数可选,需要传入属性描述符以及相应的属性名称,这些属性对应 Object.defineProperties() 的第二个参数,其中 Object.defineProperties() 的第二个参数 又可以通过 Object.getOwnPropertyDescriptors(obj) 获取。

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

// 描述符被正确拷贝
Object.getOwnPropertyDescriptors(newObj);

// 输出:
{ firstName:
{ value: 'Yancey',
writable: true,
enumerable: true,
configurable: true },
lastName:
{ get: [Function: get],
set: undefined,
enumerable: true,
configurable: true },
greeting:
{ value: [Function: greeting],
writable: true,
enumerable: true,
configurable: true },
age:
{ value: 18,
writable: false,
enumerable: false,
configurable: false } }

// 原型属性也被拷贝
newObj.__proto__; // { somePrototypeKey: 'somrthing...' }

继承新思路

创建子类的典型方法是定义子类,将其原型设置为超类的实例,然后在该实例上定义属性。下面是一种新的思路可以尝试。

function superclass() {}
superclass.prototype = {
// 在这里定义方法和属性
}
function subclass() {}
subclass.prototype = Object.create(
superclass.prototype,
Object.getOwnPropertyDescriptors({
// 在这里定义方法和属性
}),
)