为啥在使用扩展语法复制对象后 getter/setter 不再工作?
Posted
技术标签:
【中文标题】为啥在使用扩展语法复制对象后 getter/setter 不再工作?【英文标题】: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
您能解释一下为什么在这种情况下重新分配名称不起作用吗?
【问题讨论】:
【参考方案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
具有undefined
的setName
属性,因为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_obj
或 create_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)
这样的东西也行不通。以上是关于为啥在使用扩展语法复制对象后 getter/setter 不再工作?的主要内容,如果未能解决你的问题,请参考以下文章