Search code examples
javascriptdefineproperty

Javascript defineProperty and Object.keys return dilemma


/**
 * @author paula
 */
var myObj = {};
Object.defineProperty(myObj, "prop1", {
           get: function(){return this._prop1;}, 
           set:function(value){this._prop1 = value;}, 
           enumerable:true});

Object.keys(myObj)    // Note that returns ["prop1"]
myObj.prop1 = "Hello"; // call set
Object.keys(myObj);    // Returns ["prop1", "_prop1"]
myObj          // Object {prop1: "Hello", _prop1: "Hello"}

I have as a prerequisite an empty object to be populated with properties using Object. defineProperty. The dilemma is that I wanted to create only one property - ["prop1"] and in the example above, based on what Object.keys() returns, it looks like that 2 properties were created - ["prop1", "_prop1"].

Question: What is the name for "prop1" - is a property or it's kind of pseudo-property ? Is it correct to be used the name property for both "prop1" and "_prop1" ?

I also tried this solution:

var myObj1 = {};
Object.defineProperty(myObj1, "prop1", {
             get: function(){return this.prop1;}, 
             set:function(value){this.prop1 = value;}, 
             enumerable:true});
myObj1.prop1= "Bye" 

and got this error: "RangeError: Maximum call stack size exceeded" which is triggered because set calls the same code over and over again in an infinite loop. I was wondering if there is any solution to this "RangeError: Maximum call stack ..." problem ? (possible duplicate). Thanks.


Solution

  • Question: What is the name for "prop1" - is a property or it's kind of pseudo-property ? Is it correct to be used the name property for both "prop1" and "_prop1" ?

    Yes, both are properties. prop1 is an accessor property (with getters/setters) while _prop1 is a data property (simple, writable value).

    To solve your problem, just don't use an accessor property:

    Object.defineProperty(myObj, "prop1", {
    //  value: undefined,
        writable: true,
        enumerable: true
    });
    

    If you need an accessor property for some reason, store the value either in a closure variable or in a non-enumerable "hidden" property:

    (function() {
        var value;
        Object.defineProperty(myObj, "prop1", {
            get: function(){ return value; }, 
            set: function(v){ value = v; }, 
            enumerable:true
        });
    })();
    

    Object.defineProperties(myObj, {
        "_prop1": {
             enumerable: false,
             writable: true
        },
        "prop1": {
            get: function(){ return this._prop1; }, 
            set: function(value){ this._prop1 = value; }, 
            enumerable:true
        }
    });
    Object.keys(myObj) // ["prop1"]