235

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?

4
  • 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. Commented May 10, 2013 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) Commented May 10, 2013 at 19:15
  • 2
    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. Commented Apr 1, 2016 at 20:28
  • this (pun intended) is now possible in ES6, inside object literal functions you can use "this" Commented Nov 28, 2017 at 12:30

8 Answers 8

243

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

Sign up to request clarification or add additional context in comments.

5 Comments

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 True, but you could always assign it to a bound function immediately after object creation. var obj = { ... }; obj.nested.func = function() { ... }.bind(obj);
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);
Any idea why this returns undefined if an anonymous function is used instead? key2: ()=>this.key1+" works!"
@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 explains it nicely.
68

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)

Comments

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

Comments

44

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!

1 Comment

I like the self-initializing object - way. THank you
26

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!'

1 Comment

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

1 Comment

This actually answers the question. The question doesn't ask about after initialization.
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());

5 Comments

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.
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.
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.
This doesn't work when you try to access property inside an array.
Ins this case, it would work with arrow functions too.
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.