问题描述
我试图允许使用transform
方法对树结构进行转换,该方法映射树中的节点。树中的节点是使用get
类型的getter定义的,当我创建新副本并转换节点的包含值时,这些节点不会被更新。
作为示例,请考虑以下简化版本:
function node() {
return {
foo: '123',get blah() { return this.foo; },transform(f) {
const copy = {};
Object.assign(copy,this);
copy.foo = f(copy.foo);
return copy;
}
};
}
const a = node();
const b = a.transform(value => '456');
a.blah
b.blah
即使foo
已更新为'456',变换后的副本中的吸气剂仍会返回'123'。
返回对象的转换后的副本,但使用getter引用更新后的对象而不是源的正确方法是什么?
解决方法
Object.assign
将调用getter;它们将不再存在:
function node() {
return {
foo: '123',get blah() { console.log('invoked');return this.foo; },transform(f) {
const copy = {};
Object.assign(copy,this);
copy.foo = f(copy.foo);
return copy;
}
};
}
const a = node();
const b = a.transform(value => '456');
console.log(Object.getOwnPropertyDescriptor(b,'blah'));
您可以使用getOwnPropertyDescriptors
复制描述符,然后使用Object.defineProperties
分配描述符:
function node() {
return {
foo: '123',get blah() { return this.foo; },transform(f) {
const copy = {};
const descriptors = Object.getOwnPropertyDescriptors(this);
Object.defineProperties(copy,descriptors);
copy.foo = f(copy.foo);
return copy;
}
};
}
const a = node();
const b = a.transform(value => '456');
console.log(a.blah);
console.log(b.blah);
,
我将首先使用node
函数本身来返回副本,即它可以创建具有任何foo
值的新鲜的不可变对象:
function node(foo) {
return {
foo,transform(f) {
return node(f(this.foo));
}
};
}
const a = node('123');
const b = a.transform(value => '456');
console.log(a.blah);
console.log(b.blah);
,
就我个人而言,我只会使用类似以下的构造函数:
function Node(){
let wow = 'will not change length'; // I like that
this.foo = '123';
Object.defineProperties(this,{ //put all getters and setters in here
blah:{
get:()=>this.foo
},length:{
get:()=>{
let l = 0;
for(let i in this)l++;
return l;
}
}
});
}
const a = new Node,b = new Node;
b.foo = '456'; a.newProp = 'test';
console.log(a.blah); console.log(a.length); console.log(b.blah); console.log(b.length);
但是,您可能要使用class
:
class Node{
constructor(){
this.foo = '123';
}
get blah(){
return this.foo;
}
get length(){
let l = 0;
for(let i in this)l++;
return l;
}
}
const a = new Node,b = new Node;
b.foo = '456'; a.newProp = 'test';
console.log(a.blah); console.log(a.length); console.log(b.blah); console.log(b.length);
我个人更喜欢构造函数,因为您可以在所有浏览器中拥有私有变量。