10

I'm deeply confused by the behaviour of either JavaScript, or the Chrome console. Can someone help me understand?

Basically I have the following JavaScript code, not nested inside any function or other scope:

var initial_array = [];

function initialiseArray() { 
   initial_array = [2, 9, 8, 6, 0, 2, 1];
} 

function copyToNewArray() {
    var copied_array = [];

    console.log("COPIED 1", copied_array);

    for (var i = 0; i < initial_array.length; i++) {
        var copy = initial_array[i];
        copied_array.push(copy);
    }

    console.log("COPIED 2", copied_array);
}

initialiseArray();
copyToNewArray();

I would expect COPIED 1 to print [] - as the variable hasn't been assigned yet - but instead it prints [2, 9, 8, 6, 0, 2, 1] - ie the value after it has been assigned.

Why?

Incidentally, if you replace lines 8-11 with initial_array = copied_array, then RESULTS 1 does indeed print as []. Is it something to do with using .push?

Ry-
  • 218,210
  • 55
  • 464
  • 476
Richard
  • 31,629
  • 29
  • 108
  • 145
  • Interesting. Does this seem to address the same issue? [link](http://zef.me/2843/javascript-the-scope-pitfall) – j08691 Jan 05 '12 at 18:15
  • 4
    similar question : http://stackoverflow.com/questions/4057440/is-chromes-javascript-console-lazy-about-evaluating-arrays – xkeshav Jan 05 '12 at 18:16

8 Answers8

8

Try debugging your issue in the Chrome script debugger. Put a breakpoint on the line:

for (var i = 0; i < initial_array.length; i++) {

and you will see the behaviour you desire.

The problem you are having is you are making the incorrect assumption that the Chrome debugger 'prints' the value immediately when in fact it does the console.log asynchronously. Since arrays are passed around by reference in the backend when it actually goes to print the value it is now the one you are seeing.

BeRecursive
  • 6,286
  • 1
  • 24
  • 41
3

Since arrays are passed by reference, every change you make to it will change what is output in the console. It is partly the behavior of Chrome's console, partly JavaScript's.

If you want to print the result at the time of the call to console.log, you could output it as a string using JSON.stringify.

console.log("COPIED 1", JSON.stringify(copied_array));

Important edit

It seems I was mostly wrong. As diEcho pointed out in the question's comments, a similar question has a better answer. It seems to be solely Chrome behavior.

Community
  • 1
  • 1
Alex Turpin
  • 46,743
  • 23
  • 113
  • 145
  • I also find this behaviour in firefox web developer console (the stock browser one, not firebug). So maybe it is not only a Chrome behavior. – Rui Marques Jan 24 '13 at 19:30
2

console.log("COPIED 1", JSON.stringify(copied_array));

Should be fine for debugging


it's a BUG you have mentioned, see below

https://bugs.webkit.org/show_bug.cgi?id=35801

also read similar questions

Is Chrome's JavaScript console lazy about evaluating arrays?

Bizarre console.log behaviour in Chrome Developer Tools

Why does javascript object show different values in console in Chrome, Firefox, Safari?

Community
  • 1
  • 1
xkeshav
  • 53,360
  • 44
  • 177
  • 245
1

It's the way arrays are displayed in the Chrome console, and that's by reference. If you want accurate results, convert to a string:

var initial_array = [];

function initialiseArray() { 
   initial_array = [2, 9, 8, 6, 0, 2, 1];
} 

function copyToNewArray() {
    var copied_array = [];

    console.log("COPIED 1", copied_array.toString());

    for (var i = 0; i < initial_array.length; i++) {
        var copy = initial_array[i];
        copied_array.push(copy);
    }

    console.log("COPIED 2", copied_array.toString());
}

initialiseArray();
copyToNewArray();

You can test this out pretty easily:

var x = [];
console.log(x), x.push(5), x; // outputs [5] and [5]
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Still, you'd think it'd render/stringize/resolve the array reference at the proper point, otherwise `console.log`ging arrays would pretty much never work. Would Firefox succumb to this too, or is it more intelligent? – Lightness Races in Orbit Jan 05 '12 at 18:17
  • @LightnessRacesinOrbit: Not more intelligent, but rather converts everything to strings, so yes it should work. (FireBug, on the other hand... I'm not sure.) – Ry- Jan 05 '12 at 18:19
  • I propose that that _is_ "more intelligent", because keeping the reference bound so late in this case clearly violates POLS. (FWIW, I meant to say Firebug) – Lightness Races in Orbit Jan 05 '12 at 18:23
  • To copy why not use `arr.slice()`? – pimvdb Jan 05 '12 at 18:28
1

The console is actually asynchronous. Because you're logging a reference to an object, by the time the object is logged it has already changed.

You could clone the array before logging it to be certain that it doesn't get changed before it gets logged.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
0
var initial_array = [];
 function initialiseArray() { 
   initial_array = [2, 9, 8, 6, 0, 2, 1];
} 
function copyToNewArray() {
    var copied_array = [];
    console.log("COPIED 1", copied_array);
    alert(copied_array.length);
    for (var i = 0; i < initial_array.length; i++) {
        var copy = initial_array[i];
        copied_array.push(copy);
    }
    console.log("COPIED 2", copied_array);
}
initialiseArray();
copyToNewArray();

Adding the line alert(copied_array.length); will show correct results.

What happens is that the log is not synchronized with the javascript execution. When the log prints the values were already changed.

Rogel Garcia
  • 1,895
  • 14
  • 16
0

that's because the copied_array is a reference, and console.log is executed asynchonously, so the content of the array is modifed before the first log prints it.

you may copy the array before printing

console.log([].concat(copied_array));
qiao
  • 17,941
  • 6
  • 57
  • 46
0

If you want to keep the console's functionality such as expanding objects in an array, I suggest using .slice, which makes a copy of the array which doesn't change when logging:

console.log("COPIED 1", copied_array.slice());
pimvdb
  • 151,816
  • 78
  • 307
  • 352