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

流过昼夜 提交于 2019-12-25 01:48:44

问题


This is my first attempt at using Javascript's Object.defineProperty and/or defineProperties and apparently I'm doing it completely incorrectly. I am passing a cfg object into a Javascript function constructor, then inside that constructor am looping over the config and

  1. Calling Object.defineProperty on each cfg key
  2. Assigning this[key] = cfg[key]

However every this[key] emerges with undefined as it's value. I've tried a few different things, such as setting the writable attribute to true, but got an error that this conflicts with the set/function. I've also tried using Object.defineProperties with the same result. I don't think it's a scoping issue with this and have even used .bind(this) on the setters to be sure, but to no avail. The code is can be run at https://repl.it/@dexygen/js-define-properties and I am including it below inline. Expected result of course rather than undefined is that this.foo e.g. will contain the string 'foo' from cfg.foo passed into the object constructor function

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];
    }
    */
}

回答1:


When you use setter or getter, then it cannot have the same property name as the returned property. It seems that you try to do something like this:

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

The property that is set or got by setter or getter must have different property name, eg _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);
}

or you can just store the values in the getters like this:

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);
}



回答2:


I want to thank the first person to comment but he's declined to answer so I am posting my own based not only on his comment (the fact that I had not providde a getter more or less being the reason I was getting back undefined from my setter), but also addressing issues brought up by some of the other answers, one being the questioning of my use case, which I address with a comment in the setter.

Working code here: 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);
}



回答3:


Here is an alternative JavaScript backward compatible equivalent

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);
}



回答4:


Why is it that a nice and clean conventional JavaScript doesn't work for you?

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 ) );


来源:https://stackoverflow.com/questions/47367205/assigning-object-properties-in-a-loop-and-including-a-setter-results-in-undefi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!