【问题标题】:Why are getter/setter no longer working after copying an object with the spread syntax?为什么在使用扩展语法复制对象后 getter/setter 不再工作?
【发布时间】:2019-01-12 12:16:38
【问题描述】:

在下面的 sn-p 中,展开语法的工作方式我不太明白:

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

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

【问题讨论】:

    标签: javascript object spread-syntax


    【解决方案1】:

    选择的答案是错误的。

    • getter/setter 不是方法,它是特殊属性。
    • ...spread 和 object.assign 将不起作用,因为它们将 getter/setter 视为普通的可枚举属性,它们 复制它的“值”,因此 getter/setter 将失败。

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

    这是我的分配代码:

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

    【讨论】:

      【解决方案2】:

      除了@CertainPerformances 的解释之外,我还要补充一点,当使用 getter/setter 时,这种行为似乎更明智,因为 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)

      【讨论】:

        【解决方案3】:

        传播对象不会复制 getter 和 setter 方法 - 相反,传播操作仅获取属性(也就是说,它调用 getter,如果有),并将结果分配给结果对象(此处为 spread_obj),就像普通对象一样,没有任何 getter 或 setter。如果您将日志语句放在 setter 和 getter 中,您可以看到这一点。生成的spread_obj 具有undefinedsetName 属性,因为setName 只是一个setter 方法,它不会返回任何内容。

        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 属性,而不是标准调用上下文(即 spread_objcreate_obj)上的 .name 属性。当您尝试分配给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)

        【讨论】:

        • 我使用 obj 而不是这个,因为我认为 setter 和 getter 实际上是复制的。现在我意识到像Object.assign(SomeClassInstance.prototype, obj) 这样的东西也行不通。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-15
        • 2014-12-20
        • 1970-01-01
        • 2012-10-16
        • 1970-01-01
        • 2017-10-06
        相关资源
        最近更新 更多