Please support this book: buy it (PDF, EPUB, MOBI) or donate
9. Object.getOwnPropertyDescriptors()
This chapter explains the ECMAScript 2017 feature “Object.getOwnPropertyDescriptors()
” by Jordan Harband and Andrea Giammarchi.
9.1 Overview
Object.getOwnPropertyDescriptors(obj)
returns the property descriptors of all own properties of obj
, in an Array:
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)
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
;
}
9.3 Use cases for Object.getOwnPropertyDescriptors()
9.3.1 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 setter 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 }
9.3.2 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
));
9.3.3 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
,
}
);
9.4 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.