复制(和修改)对象时如何避免 Javascript 中的陈旧吸气剂?
How to avoid stale getters in Javascript when copying (and modifying) an object?
我正在尝试使用映射树中节点的 transform
方法允许转换树结构。树中的节点是使用 get
-type getters 定义的,当我制作新副本并转换节点包含的值时,这些节点不会更新。
例如,考虑这个简化版本:
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
转换副本中的 getter 继续 return“123”,即使 foo
已更新为“456”。
return 转换对象副本但 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
函数本身用于 return 副本开始,即它可以创建具有任何 foo
值的新的不可变对象:
function node(foo) {
return {
foo,
get blah() { return this.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);
我个人更喜欢构造函数,因为您可以在所有浏览器中拥有私有变量。
我正在尝试使用映射树中节点的 transform
方法允许转换树结构。树中的节点是使用 get
-type getters 定义的,当我制作新副本并转换节点包含的值时,这些节点不会更新。
例如,考虑这个简化版本:
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
转换副本中的 getter 继续 return“123”,即使 foo
已更新为“456”。
return 转换对象副本但 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
函数本身用于 return 副本开始,即它可以创建具有任何 foo
值的新的不可变对象:
function node(foo) {
return {
foo,
get blah() { return this.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);
我个人更喜欢构造函数,因为您可以在所有浏览器中拥有私有变量。