Object.defineProperty 重复 属性 个值

Object.defineProperty duplicates property values

我正在试验下面的一些代码,一切似乎都按预期工作。目标是将 a.val 绑定到 b.val,这样当 a.val 更改时,b.val 也会自动更改。

// instantiate 
var a = {}
var b = {}

// set value
a.val = 10

// check values
console.log("A.val is:", a.val) // 10
console.log("B.val is:", b.val) // undefined

// bind values
Object.defineProperties(a, {
  val: {
    configurable: true, // can be changed or deleted
    enumerable: true,   // can be iterated over
    get: ()=> this.val,
    set: (value)=> {
        this.val = value
        b.val = value
        return value
    }
  }
})

// reset value
a.val = 20

// check values
console.log("A.val is:", a.val) // 20
console.log("B.val is:", b.val) // 20

所以我想推进这个概念并将 Object.defineProperties 部分抽象成一个函数。我想我可以做一个 for 循环并循环遍历一个对象的属性,调用 Object.defineProperty。但是我 运行 遇到了一些问题:

function getSet(props){
  for(var key in props){
    Object.defineProperty(props, key, {
      configurable: true, // can be changed or deleted
      enumerable: true,   // can be iterated over
      get: ()=> this[key],
      set: (value)=> {
          this[key] = value
          return value
      }
    })
  }

  return props
}

var a = getSet({name: 'the letter a', age: 12})

a.name // undefined
a.name = 'the letter a'
a.age // 'the letter a'

本质上,我无意中绑定了传递给 getSet 函数的对象的两个属性。此外,我已经删除了它们的初始值。

我的问题是,为什么会发生这种情况,我如何才能更接近我的目标,即将一个对象的属性绑定到另一个对象。

通过测试其他几种可能的解决方案,我 运行 遇到了 Maximum call stack size exceeded 错误:我假设在 getter 或 setter 中引用 this 时,从我试图在其上定义属性的对象获取值的行为会触发对 getter 的调用,并无限循环。不完全确定 when/why 会发生这种情况。

您当前的代码无法正常工作。两大缺陷是

  • this 值将是模块对象(在节点中)或全局对象(window 在浏览器中),而不是当前对象。

    • 如果它按您预期的那样工作并引用了当前访问的接收者(当您尝试使用适当的方法时也会发生这种情况),您将从 a.val 中读取 a.val 的值并将 a.val 的值写入 a.val,创建您所经历的无限递归。您需要为实际存储使用其他东西 - 在您的情况下,b 对象是正确的解决方案。
    • 在浏览器中,分配给全局对象的 name 属性 会将所有内容强制转换为字符串
  • 在你的循环中,只有一个 single key variable that the getter/setter functions close over。所以他们都会在商店的同一个地方使用这个值。

要解决此问题,请使用

function getSet(proxy, store) {
  for (const key in store) {
//     ^^^^^ block scope
    Object.defineProperty(props, key, {
      configurable: true,
      enumerable: true,
      get: () => { return store[key]; },
//                        ^^^^^
      set: (value) => { store[key] = value; }
//                      ^^^^^
    });
  }
  return proxy;
}