Javascript Wonks: "missing : after property id"

I've been doing work with OpenSocial recently and have used the opportunity to bring my tolerance talent in Javascript up a notch or two. In doing so, I've been slowly but surely running into a myriad of browser-specific quirks along with a few cross-browser gems that have left me thinking about putting some browser developers on my "To Anonymously Beat Up In Alleyway" list (so far, James Gosling, and this man top the list).

After working on a few "classes" tonight (the notion that Javascript is object-oriented still makes me chuckle) I ran into an interesting problem with some of my global-level "constants" defined in the same file that I was working in, that my "class" just so happened to make use of. As I tend to do when I fall into situations like this to where I can't tell if I'm hallucinating or if something with Javascript has gone awry, I called over Sergio (in-house CSS master and Javascript Lvl. 60 Mage).

Some background to how Javascript works
Javascript engines essentially have two "modes" that it runs over your code that you can spot errors in. The first mode, "parsing", is where you'll find syntax errors spewing into the Javascript console. If you've used any interpreted language before (Python, Java, C#, Ruby), this is really just "compilation". Using Python as an example, when you import a module (i.e. import some_module) the Python interpreter actually compiles your code into Python byte-code to be executed at a later date. The second mode, "execution", is where you'll run into your run-time errors, using an accessing an undefined object property, overrunning an array index, etc. In Python/Java terms, this is where your compiled byte-code is actually being run in the Python/Java virtual machine.

The gripe
The crux of the problem comes down to two different ways to declare an associative array in Javascript, the following two notations are both correct and both "work":
Notation #1

  1. var mapped_values = {};
  2. mapped_values['key'] = 'value';

Notation #2
  1. var mapped_values = {'key' : 'value'};

Everything looks correct yes? (hint: say yes)

Incorrect, because of the point at which the two are evaluated. The first example will be notated at run-time, whereas the second example will be evaluated at parse/compile-time. Who cares right? The distinction becomes much more apparent when you start to use references to other code for your keys. Keep in mind, with both notations it is actually valid to have a key "undefined" when you declare your associative array. For example:

Notation #1

  1. /* the variable "foo" is not defined */
  2. var mapped_values = {};
  3. mapped_values[foo] = 'value';

Notation #2
  1. /* the variable "foo" is not defined */
  2. var mapped_values = { foo : 'value' };

This still works perfectly fine, both at parse/compile-time and at run-time. Since I mentioned I'm working on OpenSocial, chances are I'm going to need to reference some of the OpenSocial code. So for the next example let's say I need to create an associative array with one of the keys defined by the OpenSocial container, using the two different notations I would write something like:
Notation #1

  1. var mapped_values = {};
  2. mapped_values[opensocial.DataRequest.PeopleRequestFields.FILTER] = opensocial.DataRequest.FilterType.ALL;

Notation #2
  1. var mapped_values = {opensocial.DataRequest.PeopleRequestFields.FILTER : opensocial.DataRequest.FilterType.ALL};

Because of the different points in time at which the two notations above will be evaluated, #1 will properly "compile" and then execute correctly when called (regardless of the scope-level at which it is defined). The second one however, will fail to "compile" when the browser's rendering engine is loading the Javascript (also regardless of the scope-level at which it is defined), and will result in the following error at load-time:

"missing : after property id"
(verified in both IE6 and Firefox)

The issue here is that the variable "opensocial" is not defined at "compile-time" as far as the Javascript engine is concerned (which is fine) but the code attempts to access a property of that object, which causes the error at "compile-time". This will error at at *any* level (as far as I've tried) so it's not a scoping issue, just an unfortunate fact of life with how Javascript is loaded and eventually executed in the browser.

Sergio and I tried a few more examples (the scope of which was in side a function, not at the global level):

  1. // Works in IE/FF
  2. var test = { magic : 'thing' };
  3. var test2 = {};
  4. test2[rick.roll] = 'thing';
  5. test2[alert] = 'somethingelse';
  6. var test3 = {};
  7. test3[function() { alert('sux'); }] = 'test';

  1. // Fail in IE/FF
  2. var test4 ={ opensocial['DataRequest']['PeopleRequestFields']['FILTER'] : opensocial['DataRequest']['FilterType']['ALL']} ;
  3. var test5 = { rick['roll'] : 'thing'};
  4. var test6 = { rick.roll : 'thing'};
  5. var test7 = { function() { alert('sux'); } : 'test'};

Object accessor calls work in "test2" for example because that's going to be evaluated at run-time instead of at compile-time, as is happening in "test5" and "test6". I would love to be proven wrong on our analysis of the issue here (our tests were less than scientific, and there may have been Corona involved) but switching from inline-declarations for associative arrays (var t = {'k' : 'v'};) to the more sequential alternative (var t = {}; t['k'] = 'v';) solved the issue of the Javascript engine's parser spewing errors on the loading of the Javascript.

Food for thought I suppose.

Comments

thanks

i understood about object notation.

Very Helpful

Thank you for this, sequential declaration solution is just what I was looking to find.

Array to get dynamic options ...

Thanks for your post! Irregardless of the commentary above, your post helped me find the solution to my problem. I'm new to javascript and jQuery, but am a long time C/C++/perl/php guy, which seems to make my brain incompatible with java.

Anyhooo... I'm replying so that maybe someone will see an example of its usage:

The autocomplete plugin has an ajax call to the php feeder script with GET/POST of q=searchterm ... well, the q is hard coded and I wanted it to be dynamic so I could easily reuse my php code for various autocomplete calls...
{
data: $.extend({
q: lastWord(term),
limit: options.max
}, extraParams),
... }

becomes:

var extendedOptions={};
extendedOptions[options.queryLetter]=lastWord(term);
extendedOptions['limit']=options.max;

{
data: $.extend( extendedOptions, extraParams),
... }

It took me a few minutes to learn that $.extend( {extendedOptions}, was incorrect, giving me an invalid initializer error.

Solution right, conclusion wrong

I respect your attempt to document the related issue, but the conclusion is wrong and needlessly elaborate. Compile-time/run-time are irrelevant.

The problem is that within an object literal, key names are validated as string literals, not evaluated as variables (for comparison, key *values* will be evaluated if they are unquoted and non-numeric).

Neither nor nor nor are valid key names within an object literal definition. Period. It doesn't matter whether exists at the time that the code is compiled/executed. The same key names are all valid if you assign them using the square-bracketed [] notation.

--Sanford

Going to take down or edit this article?

I think you should edit this article to reflect the correct explanation that I noted above. Otherwise, you're doing a disservice to those that stumble upon your piece, see the seeming depth and "peer review" of your explanation, and think it's correct.

Up to you, of course... blogs aren't about retractions, I know, so why don't you just trash all the comments and rewrite it?

--Sanford

Re: Going to take down or edit this article?

I'll likely update and/or repost this weeikend, your explaination sounds about right but I want to verify it in a v8 shell with a bit more concrete demonstration than "Sanford says so"

Re: Going to take down or edit this article?

Understood, I was not expecting you to edit without retesting. Believe you will find the experiment is very simple to design. Don't know how exactly you went awry before, you must have been off on the wrong foot from start.

--Sanford

Tags were stripped...

Last ¶ should read:

"Neither «object.property» nor «object['property']» nor «function(args)» are valid key names within an object literal definition. Period. It doesn't matter whether «object.property» exists at the time..."

Interesting ...

I am reasonably sure I have run into that error message before when using JSON objects. The error is generally inferring that the paring of name/value is "corrupt" in some way ... usually because of funky characters in either side of the pair. Perhaps it is not (directly) an issue of scope, but one of syntax-- solved by the "Associative Array" notation vs the JSON/Object common style notation?

Now it's only just gone 4:00pm here, so please bear in mind that my analysis does not yet benefit from the "Corona" effect :)

thnx

i have run into the same problem today and this article helped me solve the issue. great thanks for sharing this with others :)

Re: thnx

Yes - thanks!

I've been getting lazy with the JSON-esque {} and [] notations for variables of late. Not being able to use "constants" in the manner you've described is a handicap, but the old-fashioned spoonfeed does work.