The Object getOwnPropertyDescriptors() method
By Flavio Copes
Learn how the JavaScript Object.getOwnPropertyDescriptors() method returns descriptors for all own properties, so you can copy getters and setters correctly.
This method returns all own (non-inherited) properties descriptors of an object.
Object.getOwnPropertyDescriptors(obj) accepts an object, and returns a new object that provides a list of the descriptors.
What is a property descriptor?
Every property in a JavaScript object carries some metadata along with it. That metadata is the descriptor.
For a data property, the descriptor holds the value plus three flags: writable, enumerable and configurable. For an accessor property, it holds the get and set functions instead of a value.
You normally never see this metadata. Object.getOwnPropertyDescriptors() makes it visible, for every own property at once.
Example:
const dog = {}
Object.defineProperties(dog, {
breed: {
value: 'Siberian Husky'
}
})
Object.getOwnPropertyDescriptors(dog)
/*
{
breed: {
value: 'Siberian Husky',
writable: false,
enumerable: false,
configurable: false
}
}
*/
Notice the three flags are all false here. When you define a property with Object.defineProperties(), every attribute you don’t specify defaults to false. Properties created with a plain assignment get true for all three instead.
Why is this method useful?
There is one use case that makes this method very useful. ES2015 gave us Object.assign(), which copies all enumerable own properties from one or more objects, and return a new object. However there is a problem with that, because it does not correctly copies properties with non-default attributes.
If an object for example has just a setter, it’s not correctly copied to a new object, using Object.assign(). For example with this object:
const person1 = {
set name(newName) {
console.log(newName)
}
}
This copy attempt won’t work:
const person2 = {}
Object.assign(person2, person1)
Why? Object.assign() reads the value of each property and assigns it to the target. Reading a property that only has a setter returns undefined. So person2 ends up with a plain name property whose value is undefined. The setter function is lost.
But this will work and copy over the setter correctly:
const person3 = {}
Object.defineProperties(person3,
Object.getOwnPropertyDescriptors(person1))
As you can see with a console test:
person1.name = 'x'
"x"
person2.name = 'x'
person3.name = 'x'
"x"
person2 misses the setter, it was not copied over.
The same limitation goes for shallow cloning objects with Object.create(). When you need a faithful copy, pass the descriptors along:
const clone = Object.create(
Object.getPrototypeOf(person1),
Object.getOwnPropertyDescriptors(person1)
)
This clones getters, setters, and all the attribute flags, not just the values.
Note this is still a shallow operation. If a property value is an object, the clone shares it with the original.
Object.getOwnPropertyDescriptors() was introduced in ES2017, and this copying problem was the main motivation for adding it to the language.
Related posts about js: