最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why are gettersetter no longer working after copying an object with the spread syntax? - Stack Overflow

programmeradmin1浏览0评论

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 badges
Add a ment  | 

3 Answers 3

Reset to default 8

Spreading 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)

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论