Search code examples
javascriptdefineproperty

Self references in Object.defineProperties()


When adding multiple properties to an object, adding them with Object.defineProperties() usually produces cleaner and more readable code than adding them one by one or in groups.

However, if a property references another one, Object.defineProperties() cannot handle it. Consider a dummy example:

var foo = {};
Object.defineProperties(foo, {
    "one" : {value: 1, enumerable: true},
    "two" : {value: foo.one + 1, enumerable: true}
})
console.log(foo); // { one: 1, two: NaN }

var bar = {};
Object.defineProperties(bar,{
    "one" : {value: 1, enumerable: true}
}); 
Object.defineProperties(bar,{
    "two" : {value: bar.one + 1, enumerable: true}
});

console.log(bar); // { one: 1, two: 2 }

Now as it is clear that the second solution always works. However, one would assume that the first one could work as well, as it seems like a shorthand for the other and suggests sequential processing of the properties anyway.

I found no excplicit documentation on this.

So the questions are:

  • Is this an intended limitation, or technical complexity in the background?
  • Is it documented somewhere?
  • Is it implementation specific or standard behavior?

Edit: answer clear, and see my comment there as well. This is a simple object literal issue (a non-issue, rather :) , not directly related to Object.defineProperties().


Solution

  • The problem doesn't really have much to do with Object.defineProperties(). The issue is with the way that object initializer expressions — "object literals" is another term — are evaluated. An object initializer is an expression, and until it's evaluated, the object it's initializing doesn't really exist. Thus, in

    Object.defineProperties(foo, {
        "one" : {value: 1, enumerable: true},
        "two" : {value: foo.one + 1, enumerable: true}
    });
    

    the object initializer subexpression is

    {
        "one" : {value: 1, enumerable: true},
        "two" : {value: foo.one + 1, enumerable: true}
    }
    

    That has to be evaluated before the call to Object.defineProperties() is even made. At the point it's being evaluated, foo does not have a property called "one"; even if it did, the value at the point the object initializer is being evaluated would be that previous value, not the new value.

    This is just the way object initializer expressions work. It's not possible to directly refer to the object being initialized from within the expression. That means that even in a statement like this:

    var foo = {
        "one" : {value: 1, enumerable: true},
        "two" : {value: foo.one + 1, enumerable: true}
    };
    

    you still have the same problem.