ES proposal: Object.getOwnPropertyDescriptors()
The ECMAScript proposal “Object.getOwnPropertyDescriptors()
” by Jordan Harband and Andrea Giammarchi is part of ECMAScript 2017. This blog post explains it.
Overview
Object.getOwnPropertyDescriptors(obj)
accepts an object obj
and returns an object result
:
- For each own (non-inherited) property of
obj
, it adds a property toresult
whose key is the same and whose value is the the former property’s descriptor.
Property descriptors describe the attributes of a property (its value, whether it is writable, etc.). For more information, consult Sect. “Property Attributes and Property Descriptors” in “Speaking JavaScript”.
This is an example of using 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 } }
This is how you would implement Object.getOwnPropertyDescriptors()
:
function getOwnPropertyDescriptors(obj) {
const result = {};
for (let key of Reflect.ownKeys(obj)) {
result[key] = Object.getOwnPropertyDescriptor(obj, key);
}
return result;
}
Use cases for Object.getOwnPropertyDescriptors()
Use case: copying properties into an object
Since ES6, JavaScript already has a tool method for copying properties: Object.assign()
. However, this method uses simple get and set operations to copy a property whose key is key
:
const value = source[key]; // get
target[key] = value; // set
That means that it doesn’t properly copy properties with non-default attributes (getters, setters, non-writable properties, etc.). The following example illustrates this limitation. The object source
has a getter whose key is foo
:
const source = {
set foo(value) {
console.log(value);
}
};
console.log(Object.getOwnPropertyDescriptor(source, 'foo'));
// { get: undefined,
// set: [Function: foo],
// enumerable: true,
// configurable: true }
Using Object.assign()
to copy property foo
to object target
fails:
const target1 = {};
Object.assign(target1, source);
console.log(Object.getOwnPropertyDescriptor(target1, 'foo'));
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
Fortunately, using Object.getOwnPropertyDescriptors()
together with Object.defineProperties()
works:
const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
console.log(Object.getOwnPropertyDescriptor(target2, 'foo'));
// { get: undefined,
// set: [Function: foo],
// enumerable: true,
// configurable: true }
Use case: cloning objects
Shallow cloning is similar to copying properties, which is why Object.getOwnPropertyDescriptors()
is a good choice here, too.
This time, we use Object.create()
that has two parameters:
- The first parameter specifies the prototype of the object it returns.
- The optional second parameter is a property descriptor collection like the ones returned by
Object.getOwnPropertyDescriptors()
.
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));
Use case: cross-platform object literals with arbitrary prototypes
The syntactically nicest way of using an object literal to create an object with an arbitrary prototype prot
is to use the special property __proto__
:
const obj = {
__proto__: prot,
foo: 123,
};
Alas, that feature is only guaranteed to be there in browsers. The common work-around is Object.create()
and assignment:
const obj = Object.create(prot);
obj.foo = 123;
But you can also use Object.getOwnPropertyDescriptors()
:
const obj = Object.create(
prot,
Object.getOwnPropertyDescriptors({
foo: 123,
})
);
Another alternative is Object.assign()
:
const obj = Object.assign(
Object.create(prot),
{
foo: 123,
}
);
Pitfall: copying methods that use super
A method that uses super
is firmly connected with its home object (the object it is stored in). There is currently no way to copy or move such a method to a different object.
Further reading
JavaScript design process:
- An explanation of the TC39 process and its stages. This process governs the design and evolution of JavaScript.
- List of proposals that are currently at stage 3 or 4.
Object.getOwnPropertyDescriptors()
and property descriptors:
- ECMAScript proposal “
Object.getOwnPropertyDescriptors
” by Jordan Harband and Andrea Giammarchi - Sect. “Property Attributes and Property Descriptors” in “Speaking JavaScript”.
Comments
Post a Comment