1

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().

balage
  • 38
  • 6
  • 1
    It's intended. You'll need two separate `.defineProperties` calls. `one` is not really defined before the whole method has been executed. – Teemu Aug 18 '15 at 13:48
  • 1
    See also [Self-references in object literal declarations](http://stackoverflow.com/q/4616202/1048572). There is no `foo.one` before the `Object.defineProperties` does get called. – Bergi Aug 18 '15 at 14:00

1 Answers1

4

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.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Thanks, I was previously using getters and forgot that later tried explicit values. Therefore I didn't realize that the value will be evaluated first. Makes sense. Btw. going back to getters this works as expected (as I expect, anyway): `var foo = {};` `Object.defineProperties(foo, {` `"one" : {get: function(){return 1}, enumerable: true},` `"two" : {get: function(){return this.one + 1}, enumerable: true}` `})` `console.log(foo.one, foo.two); // 1, 2` – balage Aug 18 '15 at 14:50
  • @balage yes, that'll work, because `this.one` isn't evaluated until some time after the object is all finished. – Pointy Aug 18 '15 at 14:55