Object.defineProperty复制属性值

时间:2018-05-21 19:51:58

标签: javascript this getter-setter

我正在尝试下面的一些代码,一切似乎都按预期工作。目标是将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时,从我试图定义属性的对象获取值的行为on触发对getter的调用,并无限循环。不完全确定何时/为何会发生这种情况。

1 个答案:

答案 0 :(得分:1)

您当前的代码并没有真正起作用。两个主要缺陷是

  • cannot use arrow functions with this in an object literalthis值将是模块对象(在节点中)或全局对象(在浏览器中为window),而不是当前对象。

    • 如果它按预期工作并引用了当前访问的接收者(当您尝试使用正确的方法时也会发生这种情况),您将从a.val读取a.val的值并写入a.vala.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;
}