0

I have rootObject which holds childObject as a value. I use two for loops to get values from childObject and put them to array. Array is cleared in every iteration of outer loop.

var childObject = new Object();
for (var i = 0; i < 3; ++i) {
    childObject[i] = i*i;
}

var  rootObject = new Object();
rootObject[0] = childObject;

I am using console.log(resultArray) to observe array. And this is what I got:

When clearing before second for loop

var resultArray = []
for ( var rootKey in rootObject){
    resultArray.length = 0; //clearing array
    for ( var childKey in rootObject[rootKey]){
       resultArray.push([ parseInt(childKey), rootObject[rootKey][childKey] ]);
    }
    console.log(resultArray);
}

I get [Array[2], Array[2], Array[2]

When clearing after second for loop

var resultArray = []
for ( var rootKey in rootObject){
    for ( var childKey in rootObject[rootKey]){
       resultArray.push([ parseInt(childKey), rootObject[rootKey][childKey] ]);
    }
    console.log(resultArray);
    resultArray.length = 0; //clearing array
}

I get []

Why result is different?

EDIT

I am using Firefox 29

http://jsfiddle.net/xf78k/5/ <-- good

http://jsfiddle.net/xf78k/6/ <-- bad

nervosol
  • 1,295
  • 3
  • 24
  • 46
  • It's a chrome console.log thing. If you have two assignments to one variable, chrome's console.log will only show the latter one. Such as: var a = 1; console.log(a); a = 2; Chrome will output 2. – Jack Song Jun 13 '14 at 15:02
  • This might be a problem with your browser's console - some display "live" results, because you're logging a reference to an array (and then modifying that reference immediately after). Try doing `console.log(JSON.stringify(resultArray))` instead – Ian Jun 13 '14 at 15:02
  • Note that manually putting the two operations after eachother makes the console behave like expected: http://jsfiddle.net/xL6F4/ – David Hellsing Jun 13 '14 at 15:03

2 Answers2

4

You store a reference to the array into your var, and print it through the console, that will show you the realtime (dynamic) state of the array. In other words, the console will show you three times the same objects, in both cases, and its state will be the final state of resultArray.

If you converted it to string, or printed its length, you'd have the expected result, because it would be a primitive value, and the console wouldn't keep track of its reference.

Taste the difference:

var childObject = new Object();
for (var i = 0; i < 3; ++i) {
    childObject[i] = i*i;
}

var  rootObject = new Object();
rootObject[0] = childObject;

var resultArray = []
for ( var rootKey in rootObject){
    for ( var childKey in rootObject[rootKey]){
       resultArray.push([ parseInt(childKey), rootObject[rootKey][childKey] ]);
    }
    console.log(resultArray.length);
    resultArray.length = 0; //clearing array
}

One suggestion: don't initialize plain objects with "new Object()".

var childObject = {};

is to be preferred instead.

EDIT: why you'd rather prefer the literal syntax to init objects

Try this code:

var a = new Object(1);
var b = new Object("1"); 

The result is that a is a Number(), and b is a String, because Object accept an optional parameter that drives which constructor is used for the object.

So, it is error prone.

Now try this:

Object = function () {
    //xmlhttp=new XMLHttpRequest("malicious site"); ...         
    console.log("XSS attack") 
} 
var c = new Object(); 

any script can override it, while {} is safer.

Finally, due to JS engines optimization, the literal syntax leads to better performance.

More

Community
  • 1
  • 1
mlr
  • 886
  • 6
  • 15
  • All wrong with the `new Object()` constructor. Try this code: `var a = new Object(1);var b = new Object("1");` The result is that `a` is a Number(), and 'b' is a String, because `Object` accept an optional parameter that drives which constructor is used for the object. So, it is error prone. – mlr Jun 13 '14 at 15:59
  • Now try this: `Object = function () { //xmlhttp=new XMLHttpRequest("malicious site"); ... console.log("XSS attack") } function () { //xmlhttp=new XMLHttpRequest("malicious site"); ... console.log("XSS attack") } var c = new Object();` any script can ovverride it, while `{}` is safer – mlr Jun 13 '14 at 16:00
  • I had to double check, but I was sure enough that the literal syntax leads to better performance (code optimization by the engine), and indeed this is the case. Even more: http://stackoverflow.com/questions/14226299/javascript-object-constructor-vs-object-literal – mlr Jun 13 '14 at 16:04
  • It’s kind of amusing that lecturing micro-optimizations of syntax takes up almost 50% of your answer :) I know I’m to blame. The constructor argument is a *feature* in my world, but I know I can’t win the speed argument (if you are creating millions of objects for memory consumption). – David Hellsing Jun 13 '14 at 21:47
  • Also, if someone has access to inject code into your program, overriding `Object` would be the least of your worries :) (I +1 your answer, fyi) – David Hellsing Jun 13 '14 at 22:02
  • I believe it's hard to consider something a feature when it's so error prone. Should you ever need that kind of behaviour, you can always have the same result using the proper constructors and switch/ifs. Might be more verbose, but it is better for maintenance. Then again, everyone is free to mess with one's own code:) I would use the literal form (and apparently I'm not alone), but as I said upfront, it's a suggestion Thanks for +1 :D – mlr Jun 14 '14 at 21:20
0

console.log does lazy and async evaluation of variables. Since arrays are passed by reference, it's not strange for it to reflex the value it has after clearance.

If you insert a breakpoint before clearing you should see the array with its elements.

ffflabs
  • 17,166
  • 5
  • 51
  • 77