Categories
javascript object-literal

Self-references in object literals / initializers

874

Is there any way to get something like the following to work in JavaScript?

var foo = {
    a: 5,
    b: 6,
    c: this.a + this.b  // Doesn't work
};

In the current form, this code obviously throws a reference error since this doesn’t refer to foo. But is there any way to have values in an object literal’s properties depend on other properties declared earlier?

0

    960

    Well, the only thing that I can tell you about are getter:

    var foo = {
      a: 5,
      b: 6,
      get c() {
        return this.a + this.b;
      }
    }
    
    console.log(foo.c) // 11

    This is a syntactic extension introduced by the ECMAScript 5th Edition Specification, the syntax is supported by most modern browsers (including IE9).

    13

    • 37

      Very helpful answer. More info on ‘get’ can be found here: developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/…

      – jake

      Feb 2, 2013 at 16:21

    • 64

      Beware that with this solution if the values of foo.a or foo.b are changed then the value of foo.c will also change in synchronism. This may or may not be what is required.

      – HBP

      May 2, 2015 at 6:21

    • 3

      @HBP That would be the exact same thing as what would happen in the question, so it seems to me like that is exactly what the expected result had to be.

      – Randy

      Mar 11, 2016 at 14:28

    • 14

      Note that this binds to the deepest nested object. E.g.: ... x: { get c () { /*this is x, not foo*/ } } ...

      Apr 18, 2018 at 20:22

    • 3

      To complete my above statement, since foo is beeing declared as a variable and c will only be evaluated at the time it is invoked, using foo inside c will work, as opposed to this (be careful though)

      Apr 19, 2018 at 20:11

    346

    You could do something like:

    var foo = {
       a: 5,
       b: 6,
       init: function() {
           this.c = this.a + this.b;
           return this;
       }
    }.init();
    

    This would be some kind of one time initialization of the object.

    Note that you are actually assigning the return value of init() to foo, therefore you have to return this.

    15

    • 108

      you can also delete this.init before return this so that foo is not poluted

      Jul 26, 2011 at 1:12

    • 17

      @BillyMoon: Yes indeed, although doing so impacts performance of all subsequent property accesses on that object, on many engines (V8, for instance).

      May 19, 2014 at 7:30

    • 8

      @MuhammadUmer: Not sure how ES6 classes are relevant to the question.

      Jun 1, 2015 at 3:02

    • 8

      @MuhammadUmer: classes are just syntactic sugar for constructor functions, so they don’t really provide anything new. Either way, the main focus of this question are object literals.

      Jun 1, 2015 at 3:45

    • 3

      @akantoword: Great 🙂 since object literals are a single expression, the init() call was directly appended the literal to keep it a single expression. But of course you can call the function separately of you want to.

      Jul 26, 2016 at 19:51

    215

    The obvious, simple answer is missing, so for completeness:

    But is there any way to have values in an object literal’s properties depend on other properties declared earlier?

    No. All of the solutions here defer it until after the object is created (in various ways) and then assign the third property. The simplest way is to just do this:

    var foo = {
        a: 5,
        b: 6
    };
    foo.c = foo.a + foo.b;
    

    All others are just more indirect ways to do the same thing. (Felix’s is particularly clever, but requires creating and destroying a temporary function, adding complexity; and either leaves an extra property on the object or [if you delete that property] impacts the performance of subsequent property accesses on that object.)

    If you need it to all be within one expression, you can do that without the temporary property:

    var foo = function(o) {
        o.c = o.a + o.b;
        return o;
    }({a: 5, b: 6});
    

    Or of course, if you need to do this more than once:

    function buildFoo(a, b) {
        var o = {a: a, b: b};
        o.c = o.a + o.b;
        return o;
    }
    

    then where you need to use it:

    var foo = buildFoo(5, 6);
    

    4

    • For my own sanity, I’m trying to find some kinda of official documentation that says basically the same thing – that an object’s this is only available to methods of said object, and no other kinds of properties. Any idea where I could find that? Thanks!

      May 5, 2018 at 1:27

    • 1

      @DavidKennell: Doesn’t get more official than the specification. 🙂 You’d probably start here and follow it through. It’s fairly awkard language, but basically you’ll see in the various subclauses of Property Definition Evaluation that the object isn’t available to the operations determining the values of property initializers.

      May 5, 2018 at 9:24

    • I can’t see the browserscope results here, but this is no more the case right? In my environment, v8: delete is 10% faster and gecko : delete is just 1% slower.

      – TheMaster

      May 7, 2020 at 15:32


    • 1

      @TheMaster – Yeah, I don’t think BrowserScope is really a thing anymore. Looks like deleting isn’t as bad as it used to be, at least not in V8 (Chrome, etc.) or SpiderMonkey. Still slower, but only a tiny bit, and these things are freaky fast these days.

      May 7, 2020 at 16:02