为什么 getter/setter 在使用传播语法复制对象后不再起作用?

Why are getter/setter no longer working after copying an object with the spread syntax?

在下面的代码片段中,传播语法的工作方式我不太理解:

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

你能解释一下为什么在这种情况下名称的重新分配不起作用吗?

传播对象不会复制 getter 和 setter 方法 - 相反,传播操作仅 gets 属性(即,它 调用 getter,如果有的话),并将结果赋值给结果对象(这里是 spread_obj),就像普通对象一样,没有任何 getters 或 setters。如果将日志语句放在 setter 和 getter 中,就可以看到这一点。结果 spread_obj 有一个 undefinedsetName 属性 因为 setName 只是一个 setter 方法,而不是 return任何东西。

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

另请注意,如果您想使用代码的第二部分,您可能应该更改 setter 和 getter 方法以引用 this 而不是 obj,否则结果可能不直观;您的 getter 和 setter 方法区域总是引用 obj 上的 .name 属性,而不是 .name 属性标准调用上下文(即 spread_objcreate_obj)。当你试图赋值给spread_obj.setName时,你实际上改变了原来的obj.name属性:

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)

参考this而不是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)

除了@CertainPerformances 的解释之外,我还要补充一点,当使用 getters/setters 时,这种行为似乎更明智,更传统的做法是为 get 和 set 指定一个名称。

例如,当您的对象看起来像这样时,一切都会好一些(至少在表面上):

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)

看起来工作量很大,但由于对象传播只需要可枚举的属性,您可以使 _name 不可枚举。那么一切都显得理智一点:

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)

select答案是错误的。

  • getter/setter不是方法,是特殊属性。
  • ...spread 和 object.assign 将不起作用,只是因为它们将 getter/setter 视为普通可枚举 属性,它们 复制它的 'value',所以 getter/setter 会失败。

如果要复制,可以参考undercore.js扩展方法:

这是我的分配代码:

const assign = (ob, ...o) => {
  o.forEach(obj=>{if (typeof obj !== 'undefined')
  Object.defineProperties(ob, Object.getOwnPropertyDescriptors(obj));}) 
  return ob;
};