In the following snippet, the spread syntax works in a manner that I don't quite understand:
let obj = {
set setName(name){
obj.name = name
},
get myName() {
return obj.name
}
}
obj.setName = 'Jon Doe'
let spread_obj = {...obj}
spread_obj.setName = 'Marion Luke'
console.log('spread_obj name', spread_obj.myName) // spread_obj name Jon Doe
let create_obj = Object.create(obj)
create_obj.setName = 'Marion Luke'
console.log('create_obj name', create_obj.myName) // create_obj name Marion Luke
In the following snippet, the spread syntax works in a manner that I don't quite understand:
let obj = {
set setName(name){
obj.name = name
},
get myName() {
return obj.name
}
}
obj.setName = 'Jon Doe'
let spread_obj = {...obj}
spread_obj.setName = 'Marion Luke'
console.log('spread_obj name', spread_obj.myName) // spread_obj name Jon Doe
let create_obj = Object.create(obj)
create_obj.setName = 'Marion Luke'
console.log('create_obj name', create_obj.myName) // create_obj name Marion Luke
Can you explain why the reassignment of the name does not work in such a case?
Share Improve this question edited Sep 6, 2018 at 23:52 Patrick Roberts 52k10 gold badges117 silver badges162 bronze badges asked Aug 5, 2018 at 7:34 jeremyTobjeremyTob 16110 bronze badges3 Answers
Reset to default 8Spreading an object does not copy getter and setter methods - rather, the spread operation only gets the property (that is, it calls the getter, if there is one), and assigns the result to the resulting object (the spread_obj
, here), just like a normal object, without any getters or setters. You can see this if you put log statements inside the setter and getter. The resulting spread_obj
has a setName
property of undefined
because setName
is only a setter method, which doesn't return anything.
let obj = {
set setName(name){
console.log('setting ' + name);
this.name = name
},
get myName() {
console.log('getting');
return this.name
}
}
obj.setName = 'Jon Doe'
console.log('About to spread');
let spread_obj = {...obj}
console.log('Done spreading. spread_obj contents is:');
console.log(spread_obj);
spread_obj.setName = 'Marion Luke' // No "setting" log statement
Also note that if you want to use the second part of your code, you should probably change the setter and getter methods to refer to this
rather than to obj
, otherwise the results could be unintuitive; your getter and setter methods area always referring to the .name
property on obj
, rather than the .name
property on the standard calling context (that is, to spread_obj
or to create_obj
). When you try to assign to spread_obj.setName
, you actually change the original obj
's .name
property:
let obj = {
set setName(name){
obj.name = name
},
get myName() {
return obj.name
}
}
obj.setName = 'Jon Doe'
let create_obj1 = Object.create(obj)
create_obj1.setName = 'Marion Luke1'
let create_obj2 = Object.create(obj)
create_obj2.setName = 'Marion Luke2'
// "Marion Luke2", but we're logging the property from the "first" object!
console.log('create_obj name', create_obj1.name)
Fix by referring to this
instead of obj
:
let obj = {
set setName(name){
this.name = name
},
get myName() {
return this.name
}
}
obj.setName = 'Jon Doe'
let create_obj1 = Object.create(obj)
create_obj1.setName = 'Marion Luke1'
let create_obj2 = Object.create(obj)
create_obj2.setName = 'Marion Luke2'
console.log(create_obj1.name)
the select answer is wrong.
- getter/setter is not method, it is special properties.
- ...spread and object.assign will not work, just because they treat getter/setter like normal enumerable property, they copy the 'value' of it, so getter/setter will fail.
if you want to copy it, you can refer undercore.js the extend method:
this is my assign code:
const assign = (ob, ...o) => {
o.forEach(obj=>{if (typeof obj !== 'undefined')
Object.defineProperties(ob, Object.getOwnPropertyDescriptors(obj));})
return ob;
};
In addition to @CertainPerformances explanation, I would add that the behavior seems more sensible when using the getters/setters a more traditionally by having one name for both get and set.
For example when your object looks like this, everything works a little better (at least on the surface):
let obj = {
set name(name){ // setter and getter are accessed with the same property
this._name = name
},
get name() {
return this._name
}
}
obj.name = 'Jon Doe'
let spread_obj = {...obj}
spread_obj.name = 'Marion Luke'
// this seems more expected
console.log('spread_obj name', spread_obj.name)
// but this is still a little strange because
// Jon Doe is still there
console.log(spread_obj)
It's seems like a lot of work, but since object spread only takes enumerable properties, you can make _name
non-enumerable. Then everything seems a little more sensible:
let obj = {
set name(name){
this._name = name
},
get name() {
return this._name
}
}
Object.defineProperty(obj, '_name', {
enumerable: false,
writable: true,
configurable: true
});
obj.name = 'Jon Doe'
let spread_obj = {...obj}
spread_obj.name = 'Marion Luke'
// now spread_obj only has a name prop:
console.log(spread_obj)
// and obj too:
console.log(obj)