I'm reading the MDN docs on Object.assign()
and came across one phrase that I don't understand:
The Object.assign() method only copies enumerable and own properties from a source object to a target object. It uses [[Get]] on the source and [[Set]] on the target, so it will invoke getters and setters. Therefore it assigns properties versus just copying or defining new properties. This may make it unsuitable for merging new properties into a prototype if the merge sources contain getters. For copying property definitions, including their enumerability, into prototypes Object.getOwnPropertyDescriptor() and Object.defineProperty() should be used instead.
Particularly this line:
This may make it unsuitable for merging new properties into a prototype if the merge sources contain getters.
I'm not exactly sure what a good example is for advocating against using Object.assign
.
A getter is an accessor function for a property that returns the property's value. This is what an object with a getter looks like:
var obj = {
get example() {
console.log("getter was called");
return Math.floor(Math.random() * 100);
}
};
console.log(obj.example);
// Note no () ---------^
Notice that when we read the value of the example
property, the function gets run even though it doesn't look like a function call.
What that part of the MDN docs is saying is that Object.assign
will call that getter, it won't create an equivalent getter on the target object. So:
var obj = {
get example() {
console.log("getter was called");
return Math.floor(Math.random() * 100);
}
};
var obj2 = Object.assign({}, obj); // calls getter
console.log(obj2.example); // just has a simple value
console.log(obj2.example); // same value, no call
console.log(obj2.example); // same value, no call
obj
's example
property has a getter, but obj2
's example
property is just a simple value property. Object.assign
didn't copy the getter, it just grabbed the getter's current value and assigned it ot obj2.example
.
You can copy getters, just not with Object.assign
:
function copyProperties(target, source) {
Object.getOwnPropertyNames(source).forEach(name => {
Object.defineProperty(
target,
name,
Object.getOwnPropertyDescriptor(source, name)
);
});
return target;
}
var obj = {
get example() {
console.log("getter was called");
return Math.floor(Math.random() * 100);
}
};
var obj2 = copyProperties({}, obj); // calls getter
console.log(obj2.example); // calls getter
console.log(obj2.example); // calls getter
console.log(obj2.example); // calls getter
Of course, if the getter isn't designed to be copied between objects (for instance, if example
's getter explicitly used obj
), you may get unexpected results.