分配对象属性(在循环中)并包含 setter 会导致未定义的值

Assigning object properties (in a loop) and including a setter results in undefined values

这是我第一次尝试使用 Javascript 的 Object.defineProperty and/or defineProperties,显然我做错了。我将一个 cfg 对象传递给一个 Javascript 函数构造函数,然后在该构造函数中循环遍历配置和

  1. 在每个 cfg 键上调用 Object.defineProperty
  2. 正在分配 this[key] = cfg[key]

然而,每个 this[key] 都以 undefined 作为它的值出现。我已经尝试了一些不同的事情,例如将 writable 属性设置为 true,但得到一个错误,表明这与 set/function 冲突。我也尝试使用 Object.defineProperties 得到相同的结果。我不认为这是 this 的范围问题,甚至在 setter 上使用 .bind(this) 可以肯定,但无济于事。代码可以是 运行 at https://repl.it/@dexygen/js-define-properties,我将其包含在内联下方。当然,预期结果而不是 undefinedthis.foo 例如将包含来自 cfg.foo 的字符串 'foo' 传递到对象构造函数

let myObj = new ObjInstance({
    foo: 'foo',
    bar: 'bar',
    baz: 'baz'
});

function ObjInstance(cfg) {
    for (let key in cfg) {
      Object.defineProperty(this, key, {
          configurable: true,
          enumerable: true,
          // writable: true, // throws error in conjunction with set function
          set: (function(val) {
              console.log('this[' + key + ']: ' + this[key]); //undefined :(
          }).bind(this)
      });
      console.log('cfg[key]: ' + cfg[key]);
      this[key] = cfg[key];
    }
    console.log(this);
    console.log('this.foo: ' + this.foo);
    /* // if you comment this in, comment out the above for loop
    let objProperties = {}
    for (let key in cfg) {
        objProperties[key] = {
          configurable: true,
          enumerable: true,
          set: (function(val) {
              console.log('this[' + key + ']: ' + this[key]);
          }).bind(this)
        }
    }
    for (let key in cfg) {
      console.log('cfg[key]: ' + cfg[key]);
      this[key] = cfg[key];
    }
    Object.defineProperties(this, objProperties);
    for (let key in cfg) {
      console.log('cfg[key]: ' + cfg[key]);
      this[key] = cfg[key];
    }
    */
}

当您使用 setter 或 getter 时,它不能与返回的 属性 具有相同的 属性 名称。看起来你试图做这样的事情:

var obj = {
  set a(param){
    console.log(this.a); //setter try to set a setter
  },
  get a(){
    return this.a;  //RangeError: Maximum call stack
  }
};

settergetter设置或获取的属性必须有不同的属性名称,例如_foo:

let myObj = new ObjInstance({
    foo: 'foo',
    bar: 'bar',
    baz: 'baz'
});

console.log(myObj);

function ObjInstance(cfg) {
    for (let key in cfg) {
      Object.defineProperty(this,key, {
          configurable: true,
          enumerable: true,
          set:(val)=>{
            this[`_${key}`] = key;
          },
          get:()=>{
            return this[`_${key}`];
          }
      });
      console.log('cfg[key]: ' + cfg[key]);
      this[key] = cfg[key];
    }
    console.log(this);
    console.log('this.foo: ' + this.foo);
}

或者您可以像这样将值存储在 getter 中:

let myObj = new ObjInstance({
    foo: 'foo',
    bar: 'bar',
    baz: 'baz'
});

console.log(myObj);

function ObjInstance(cfg) {
    for (let key in cfg) {
      Object.defineProperty(this,key, {
          configurable: true,
          enumerable: true,
          get:()=>{
            return key;
          }
      });
      console.log('cfg[key]: ' + cfg[key]);
      this[key] = cfg[key];
    }
    console.log(this);
    console.log('this.foo: ' + this.foo);
}

为什么漂亮干净的传统 JavaScript 不适合您?

var myObj = new ObjInstance
(
  {
    foo: 'foo',
    bar: 'bar',
    baz: 'baz'
  }
);

function ObjInstance( cfg ) {
    for ( var key in cfg ) {
      this[ key ] = cfg[ key ];
    }
};

console.log( JSON.stringify( myObj ) );

myObj.bar = 'bar2';

console.log( JSON.stringify( myObj ) );

我想感谢第一位发表评论的人,但他拒绝回答,所以我不仅根据他的评论发表自己的评论(事实上我没有提供 getter 或多或少是我从 setter 回来 undefined 的原因),但也解决了其他一些答案提出的问题,一个是对我的用例的质疑,我在 [= =13=].

工作代码在这里:https://repl.it/@dexygen/javascript-defineProperty-working

let myObj = new ObjInstance({
    foo: 'foo',
    bar: 'bar',
    baz: 'baz'
});

function ObjInstance(cfg) {
    for (let key in cfg) {
      this[key] = cfg[key];

      Object.defineProperty(this, key, {
          configurable: true,
          enumerable: true,
          set: function(val) {
              cfg[key] = val;
              // additionally plan to emit an event
          },
          get: function() {
              return cfg[key];
          }
      });
    }

    console.log('this.foo: ' + this.foo);
    this.foo = 'foobarbaz';
    console.log('this.foo: ' + this.foo);
}

这是一个替代方案 JavaScript 向后兼容的等价物

function Handle( O ) {

var arr = [];

   for( var p in O ) {
    arr.push(
    ["get "+p+"(){ return O['"+p+"']; }, set "+p+"(x){ O['"+p+"'] = x; /*your emitter*/ }" ]
    );
   }
    return Function("O", "return {"+arr.join()+"}" )(O);
}