11

Consider the code below. The first console.log correctly logs the image, and you can see its properties in the image below. However, when I try logging one if its properties to the console, I get undefined!

console.log(that.data[0].cards); //works -- see image below
console.log(that.data[0].cards.E); //undefined
console.log(that.data[0].cards['E']); //undefined
console.log(that.data[0].cards.hasOwnProperty('E')); //false

var test = JSON.stringify(that.data[0]);
console.log(test); // {}

for( var key in that.data[0].cards ) {
    console.log('hello????') //doesn't appear in the console
}

console.log( Object.keys( that.data[0].cards ) ); //[]
console.log( that.data[0].cards.propertyIsEnumerable("E") ); //false
console.log( that.data[0].cards.__lookupGetter__( "E" ) ); //undefined

The result in the console:

enter image description here

Any idea what's going on here? The xml property inside of that.data[0] should also have properties inside of it -- named the same, in fact, as the properties in cards.

FWIW, I get the same thing in Firebug (the above console image is Chrome).

maxedison
  • 17,243
  • 14
  • 67
  • 114
  • 1
    Can't help you if we can't replicate the behavior. Can you provide the JSON ? – aziz punjani Oct 27 '11 at 20:56
  • 2
    Can you pass the result of JSON.stringify(that) ? – FreeCandies Oct 27 '11 at 20:54
  • `JSON.stringify(that)` doesn't seem to do anything except bring the script to a complete stop. Doesn't throw an error or anying else, but nothing runs after the call to `JSON.stringify(that)`. I've updated my question to show the results of `JSON.stringify(that.data[0].cards)`, which just shows it to be a blank object `{}`. – maxedison Oct 27 '11 at 21:15
  • 2
    Are you sure the object is really not empty this time? You didn't expand it... – pimvdb Oct 27 '11 at 21:17
  • 1
    Thanks. I'm still not able to reproduce. Could you perhaps provide a simplified, isolated test case on http://jsfiddle.net? – pimvdb Oct 28 '11 at 09:10

2 Answers2

14

I've solved the problem. Basically, the object in question (that.data[0].cards) has its properties created by a function a() that runs after all the AJAX requests for the necessary XML files have been processed. I allow the requests to run asynchronously, using a counter to determine in the success callback function if a() should be called yet.

After a() runs, function b() is supposed to perform operations on that.data[i].cards. However, b() was running prior to a() being called because of a()'s reliance on the asynchronous requests. So the solution was simply to make a() call b().

So this turned out to be a pretty simple mistake on my part. What made it so confusing was the fact that logging that.data[0].cards to the console showed me that in fact the cards object had already been built, when in fact it had not yet. So the console was providing me with incorrect--or at least unclear--information.

Thanks for everyone's help last night! Upvotes all around :)

maxedison
  • 17,243
  • 14
  • 67
  • 114
8

I think the object keys have unprintable characters, such can be replicated like this:

var obj = {};
obj["E"+String.fromCharCode(15)] = new Array(15);

console.log(obj);

/*Object
E: Array[15]
__proto__: Object*/

console.log(obj.E)

//undefined

console.log( obj["E"+String.fromCharCode(15)] )

//[]

Edit: you can see if this is the case for your object keys:

var realKeys = [];

for( var key in obj ) {
realKeys.push( [].slice.call( key ).map( function(v){return v.charCodeAt(0);} ).join(" ") );
}

//["69 15"] (69 stands for the letter "E" and 15 was the unprintable character I added manually)

Edit2: Since you can't do that I came up with another way to see if there are unprintable characters:

Copypaste the key string like this: (go all the way as much as you can on both ends so you pick any invisible characters)

Then dump your clipboard like this (Make sure you are using double quotes):

Esailija
  • 138,174
  • 23
  • 272
  • 326
  • Seems to produce the same result. I did: `console.log(that.data[0].cards['E'+String.fromCharCode(15)]);` – maxedison Oct 27 '11 at 21:06
  • That's because your object might have different invisible character than `String.fromCharCode(15)`, it could be 3, 24 or 22 and so on :) – Esailija Oct 27 '11 at 21:07
  • @macedison: And because of @Esailija's reason please copy/paste the exact result of `JSON.stringify(that.data[0])` so that it can be investigated – pimvdb Oct 27 '11 at 21:08
  • `JSON.stringify(that.data[0])` produces: `{"session":"01","xml":{},"cards":{}}`. The truth is that the `xml` object should also have properties inside of it, named the same as the `cards` object in fact. I also updated my question to include the results of the `for` loop you suggested, but it doesn't seem to even run. – maxedison Oct 27 '11 at 21:17
  • Try `console.log( Object.keys( that.data[0].cards ) );`. If that doesn't work try `console.log( that.data[0].cards.propertyIsEnumerable("E") );` If that doesn't work try `console.log( that.data[0].cards.__lookupGetter__( "E" ) );` If THAT doesn't work just link to the page :D – Esailija Oct 27 '11 at 21:23
  • I've updated my post to show the result of that. I can't link to the page because I'm doing the development locally. This is easily one of the weirdest things I've ever encountered in programming... – maxedison Oct 27 '11 at 21:32
  • How are you creating the object, where does it come from? Have you isolated it on a clean page? – Esailija Oct 27 '11 at 21:36
  • The object gets created dynamically via AJAX requests that load XML data (using jQuery). Once all the data is loaded, it is parsed (again, using jQuery). It's at this point that the supposedly "undefined" arrays (cards.E, cards.N, card.X) are created. I'll try your latest edit in the morning. Thanks for your help. – maxedison Oct 27 '11 at 22:10