What is the error in this small object?

When I load this script, FF tells me that on line 4 time: OpenPage.today.getTime(), that OpenPage is undefined. But isn’t that object defined when the page loads?


var OpenPage = {

    today: new Date(),
    time: OpenPage.today.getTime(),

    init: function(){

        alert(OpenPage.today);
        alert(OpenPage.today.getTime());

    }

}
OpenPage.init();

Hi,
This line is causing the trouble:

time: OpenPage.today.getTime(),

AFAIK, there is no way to have values in an object literal’s properties depend on other properties declared earlier (today in this case).
This also accounts for your error message. It is not OpenPage that is undefined, rather OpenPage.today

To get around this, you could use a getter:

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unbenanntes Dokument</title>
  </head>
  
  <body>
    <script>
      var OpenPage = {
        today: new Date(),
        
        get time(){
          return OpenPage.today.getTime();
        },
        
        init: function(){
          alert(OpenPage.today);
          alert(OpenPage.time);
          alert(OpenPage.today.getTime());
        }
      }
      OpenPage.init();
    </script>
  </body>
</html>

More info on getters: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/get
See also: Self-references in object literal declarations

Hey thanks again Pullo!

seems like such a subtle difference for js to fuss over :slight_smile:

i never saw getter syntax in js before, so just to make sure I’m reading this right, using ‘get’ space, then a function name i.e. time(), lets me call OpenPage.time as if I declared that property myself??

i made a similar error.

now OpenPage is undefined in footballStart, but i used a getter. but that property works in the alert below


var OpenPage = {
    
    today: new Date(),
    
    
    
    get now(){
        return OpenPage.today.getTime();
    },
    get currentYear(){
        return OpenPage.today.getFullYear();
    },
    footballStart: new Date(OpenPage.currentYear,7,9),
    
    init: function(){
        

        
        alert(OpenPage.now);
        alert(OpenPage.footballStart);
        alert(OpenPage.currentYear);
        
    },
    
}
OpenPage.init();

Ok, in reverse order:

You can’t do this for the same reason as before:

footballStart: new Date(OpenPage.currentYear,7,9)

it needs to be:

get footballStart(){
  return new Date(OpenPage.currentYear,7,9);
}

then things will work as expected.

Not quite.

In your original example, OpenPage’s time property referenced OpenPage’s today property which was not possible. By using a getter method, you are deferring this referencing until after the object is created.

lol, thanks!

I’ll check out that link you posted above.

Hi,

I was kind of between doing things when I posted my last answer, so allow me to elaborate, using a simpler example:

As you know, object literals are comma separated lists of key value pairs wrapped in curly braces.
Keys can be strings or identifiers, while values can be any valid expression, including array literals, functions, and nested object literals.
Object literals are used to organise code in a logical fashion and as a means of encapsulating data, so as to avoid polluting the global namespace.

When creating object literals, you can either first create a blank object, then add properties and methods to it.

e.g.

var object = {};
object.foo = 1;
object.bar = null;
object.baz = function() {
  console.log("hello from baz()");
};

or create everything up front, as you were doing in your example:

var object = {
  foo : 1,
  bar : null,
  baz : function(){
          return "hello from baz()";
        }
};

Now, the problem you were encountering, is that using the second notation, the object doesn’t exist until the whole block is interpreted.
This means that referencing a previously declared property from within the object, before the object is created, is problematic.

This works fine:

var object = {}
// object now exists

object.foo = 1;
object.bar = object.foo + 1;

console.log(object.bar);
=> 2

This doesn’t:

var object = {
  foo : 1,
  bar : object.foo + 1
};
// object now exists

console.log(object.bar);
=> Uncaught TypeError: Cannot read property 'foo' of undefined 

When you write object.foo + 1, object doesn’t exist at this point.

One way to get around this is to use the aforementioned getter method, thus:

var object = {
  foo : 1,
  get bar() { return object.foo + 1 }
};

console.log(object.bar);
=> 2

What you are doing here is binding an object property (bar) to a function that will be called when that property is looked up.
This function will only ever be called after the object exists in memory, so everything works as expected.

By way of further example and to address your second question, we can add a method to our object like this:

var object = {
  foo : 1,
  get bar() { return object.foo + 1 },
  baz : function(){ console.log("hello from baz()"); }
};

object.baz();
=> hello from baz()

Now, say we wanted baz() to output the value of bar, then that’s no problem as we have a getter in place:

var object = {
  foo : 1,
  get bar() { return object.foo + 1 },
  baz : function(){ console.log(object.bar); }
};

object.baz();
=> 2

But this getter, doesn’t mean that we can use bar from within a property declaration:

var object = {
  foo : 1,
  get bar() { return object.foo + 1 },
  foobar : bar + 1
};

object.foobar();
=> Uncaught ReferenceError: bar is not defined 

Here, you are still trying to access bar before the block has been interpreted and the object exists, which won’t work.

If you want to do this, you have to do the same as before and (for example) use a getter method with foobar

var object = {
  foo : 1,
  get bar() { return object.foo + 1 },
  get foobar() { return object.bar + 1}
};
console.log(object.foobar);

=> 3

So, I hope that makes a bit more sense.
I’ve written more than I had intended, so I’ll stop now.

Since getters don’t seem to be supported yet by IE, and since we now understand what is causing the problem, the issue can be easily solved by waiting until the object is defined before adding the time property to it.


var OpenPage = {
    today: new Date(),
    [s][color="red"]time: OpenPage.today.getTime(),[/color][/s]
    init: function(){
        alert(OpenPage.today);
        alert(OpenPage.[color="green"]time[/color][s][color="red"]today.getTime()[/color][/s]);
    }
}
[color="green"]OpenPage.time = OpenPage.today.getTime();[/color]

OpenPage.init();

This isn’t quite true.
According to the docs, this should work in IE9 upwards.

Yup, fair point. This is probably a better way to do things.

It seems that IE6 is now all-but officially dead. If and when we can stop supporting IE7 and IE8, we can make good use of getters and setters.