Reputation: 8520
Object.assign()
does not work with getters and setters (because it only reads the property values, as explained in this question):
> let a = {}
> Object.defineProperty(a, "foo", {get: () => "bar"})
> a.foo
'bar'
> let b = Object.assign({}, a)
> b.foo
undefined
What are my alternatives to Object.assign
here, if I want to include foo
in b
?
Upvotes: 1
Views: 1743
Reputation: 2452
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.
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Since you have only set getter
for foo
and not have any setter
it'll not be copied.
Checked out the bellow example, you might find it interesting.
a = {};
Object.defineProperty(a, "foo", {get: () => "bar"});
console.log(a.foo);
a.foo = 1
console.log(a.foo); // logs bar and not 1
Solution
Add a setter too!
var a = {};
(function(){
var fooValue = 'bar';
Object.defineProperty(a, "foo", {
get: () => {return fooValue},
set: (newFoo)=>{fooValue = newFoo},
enumerable: true // Defaults to 'false', which will not copy the property
});
})();
var b = Object.assign({}, a);
console.log(a, b);
Note: This will copy the property as is without the setter or getter method. If you have some custom logic in setter
or getter
it'd not work in the copy.
Upvotes: 1
Reputation: 386604
You need to capture setters and getter directly, because
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 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, use
Object.getOwnPropertyDescriptor()
andObject.defineProperty()
instead. [source; emp: ns]
and take some code from here.
// This is an assign function that copies full descriptors
function completeAssign(target, ...sources) {
sources.forEach(source => {
let descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
// By default, Object.assign copies enumerable Symbols, too
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}
const obj = {
foo: 1,
get bar() {
return 2;
}
};
let copy = Object.assign({}, obj);
console.log(copy);
// { foo: 1, bar: 2 }
// The value of copy.bar is obj.bar's getter's return value.
copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 14679
You can Object.create
and use prototypal inheritance:
let a = {};
Object.defineProperty(a, "foo", { get: () => "foo" });
console.log(a.foo);
let b = Object.create(a);
console.log(b.foo);
But then mutations a
(if it can have other properties besides foo
) will be automatically reflected in b
, I don't know whether that's the desired behavior. Maybe doing
let b = Object.create(a);
Object.assign(b, a);
will solve both problems.
Upvotes: 2