复制(和修改)对象时如何避免 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);

我个人更喜欢构造函数,因为您可以在所有浏览器中拥有私有变量。