226

Lets say I have the following JavaScript:

var obj = {
 key1 : "it ",
 key2 : key1 + " works!"
};
alert(obj.key2);

This errors with "key1 is not defined". I have tried

this.key1
this[key1]
obj.key1
obj[key1]
this["key1"]
obj["key1"]

and they never seem to be defined.

How can I get key2 to refer to key1's value?

Piper
  • 1,266
  • 3
  • 15
  • 26
Erin Drummond
  • 5,347
  • 6
  • 35
  • 41
  • Very old versions of Firefox (I tried Firefox 4 and it gives a warning but accepts it) allow you to use #N= and #N# to refer to existing objects in the same expression, but this doesn't work for primitives; you just have to duplicate those. – Neil May 10 '13 at 19:09
  • 1
    Actually, there's a cheesy workaround, you can box the primitive, and then refer to the boxed value with the sharp variable: `var obj = { key1: #1= (new String("it ")), key2: #1# + "works!" }; alert(obj.key2)` – Neil May 10 '13 at 19:15
  • 1
    key2 is defined inside the object while the object is being defined. So there is no key1 yet when key2 is being defined. Only after the assignment does key1 exist. You're referencing something that doesn't exist yet. – Keith Tyler Apr 01 '16 at 20:28
  • this (pun intended) is now possible in ES6, inside object literal functions you can use "this" – danday74 Nov 28 '17 at 12:30

8 Answers8

231

Maybe you can think about removing the attribute to a function. I mean something like this:

var obj = {
  key1: "it ",
  key2: function() {
    return this.key1 + " works!";
  }
};

alert(obj.key2());
adiga
  • 34,372
  • 9
  • 61
  • 83
pencilCake
  • 51,323
  • 85
  • 226
  • 363
  • 23
    it works only if you have a flat `obj`, like the one in your example. It will not work if you have nested keys. – Green Sep 29 '15 at 03:26
  • 2
    @Green True, but you could always assign it to a bound function immediately after object creation. `var obj = { ... }; obj.nested.func = function() { ... }.bind(obj);` – Vala Oct 21 '15 at 11:29
  • 54
    In ES6 you can replace `key2 : function() {...}` with `get key2() {...}` and then you don't need to use brackets when calling it: `alert(obj.key2);` –  Dec 06 '16 at 05:54
  • 2
    Any idea why this returns undefined if an anonymous function is used instead? `key2: ()=>this.key1+" works!"` – Omar Sharaki Jun 25 '20 at 08:40
  • 4
    @OmarSharaki, pencilCake's function as well as yours are both anonymous. The reason yours doesn't work is you are using an arrow function which doesn't see `this`. There's a lot more to it than I'm explaining here but the answer by Sean Vieira to [this question](https://stackoverflow.com/questions/28798330/arrow-functions-and-this) explains it nicely. – samurai_jane Aug 06 '20 at 19:09
65

This can be achieved by using constructor function instead of literal

var o = new function() {
  this.foo = "it";
  this.bar = this.foo + " works"
}

alert(o.bar)
user187291
  • 53,363
  • 19
  • 95
  • 127
47

You can't refer to a property of an object before you have initialized that object; use an external variable.

var key1 = "it";
var obj = {
  key1 : key1,
  key2 : key1 + " works!"
};

Also, this is not a "JSON object"; it is a Javascript object. JSON is a method of representing an object with a string (which happens to be valid Javascript code).

Tgr
  • 27,442
  • 12
  • 81
  • 118
42

One alternative would be to use a getter/setter methods.

For instance, if you only care about reading the calculated value:

var book  = {}

Object.defineProperties(book,{
    key1: { value: "it", enumerable: true },
    key2: {
        enumerable: true,
        get: function(){
            return this.key1 + " works!";
        }
    }
});

console.log(book.key2); //prints "it works!"

The above code, though, won't let you define another value for key2.

So, the things become a bit more complicated if you would like to also redefine the value of key2. It will always be a calculated value. Most likely that's what you want.

However, if you would like to be able to redefine the value of key2, then you will need a place to cache its value independently of the calculation.

Somewhat like this:

var book  = { _key2: " works!" }

Object.defineProperties(book,{
    key1: { value: "it", enumerable: true},
    _key2: { enumerable: false},
    key2: {
        enumerable: true,
        get: function(){
            return this.key1 + this._key2;
        },
        set: function(newValue){
            this._key2 = newValue;
        }
    }
});

console.log(book.key2); //it works!

book.key2 = " doesn't work!";
console.log(book.key2); //it doesn't work!

for(var key in book){
    //prints both key1 and key2, but not _key2
    console.log(key + ":" + book[key]); 
}

Another interesting alternative is to use a self-initializing object:

var obj = ({
  x: "it",
  init: function(){
    this.y = this.x + " works!";
    return this;
  }
}).init();

console.log(obj.y); //it works!
Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
25

Because the statement defining obj hasn't finished, key1 doesn't exist yet. Consider this solution:

var obj = { key1: "it" };
obj.key2 = obj.key1 + ' ' + 'works!';
// obj.key2 is now 'it works!'
Delan Azabani
  • 79,602
  • 28
  • 170
  • 210
  • 1
    nice solution, makes sense that the object hasn't finished it's execution phase so calling a key within it would be undefined. Objects are functions so keys within are not defined within memory until the execution phase has ended – Pixelomo Nov 15 '16 at 12:19
24

That's not a JSON object, that's a Javascript object created via object literal notation. (JSON is a textual notation for data exchange (more). If you're dealing with JavaScript source code, and not dealing with a string, you're not dealing with JSON.)

There's no way within the object initializer to refer to another key of the object being initialized, because there's no way to get a reference to the object being created until the initializer is finished. (There's no keyword akin to this or something for this situation.)

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • This actually answers the question. The question doesn't ask about after initialization. – Aaron Sep 22 '22 at 09:14
19

You can also reference the obj once you are inside the function instead of this.

var obj = {
    key1: "it",
    key2: function(){return obj.key1 + " works!"}
};
alert(obj.key2());
Jason
  • 2,280
  • 23
  • 22
  • 1
    I think this answer is superior to the 'best answer'. obj.key1 is always 'it' while this.key1 can be other value depending on where obj.key2 function is invoked. e.g. setTimeout( obj.key2, 100 ); this refers to the window object. – Mingtao Sun Nov 23 '16 at 04:15
  • this is always working solution. is there any drawback ? why people go with this ? execution context is dynamic and tricky to manipulate in some cases. – userx Jan 31 '20 at 20:22
  • I think the solution is the closest answer to the question. Simple and elegant. Sure, the other answers are good and all but this one is on point. Please let me know if there're any drawbacks of this as I'm using this concept in one of my projects. – Kartik Chauhan Feb 16 '21 at 13:44
  • This doesn't work when you try to access property inside an array. – Kartik Chauhan Feb 16 '21 at 14:13
5

This is not JSON. JSON was designed to be simple; allowing arbitrary expressions is not simple.

In full JavaScript, I don't think you can do this directly. You cannot refer to this until the object called obj is fully constructed. So you need a workaround, that someone with more JavaScript-fu than I will provide.

Thomas
  • 174,939
  • 50
  • 355
  • 478