2010-10-30 13 views
87

Voy a empezar con el código:¿La consola de JavaScript de Chrome es vaga sobre la evaluación de matrices?

var s = ["hi"]; 
console.log(s); 
s[0] = "bye"; 
console.log(s); 

simple, ¿verdad? En respuesta a esto, Firebug dice:

["hi"] 
["bye"] 

maravilloso, pero la consola JavaScript de Chrome (7.0.517.41 beta) dice:

["bye"] 
["bye"] 

he hecho algo mal, o es la consola JavaScript de Chrome ser excepcionalmente vago sobre la evaluación de mi matriz?

+1

observo el mismo comportamiento en Safari, así que probablemente sea una cosa de webkit. Bastante sorprendente. Yo lo llamaría un error. – Lee

+0

@mplungjan - eso no es verdad. la primera línea declara una matriz "simple antigua" con un solo elemento en el índice 0. La tercera línea simplemente asigna un nuevo valor a ese elemento. ambos casos están trabajando con una matriz indexada numéricamente simple. – Lee

+0

si esto es un error, no entiendo por qué este error no fue encontrado y reparado. –

Respuesta

47

Gracias por el comentario, tec. Pude encontrar un error de Webkit existente no confirmado que explica este problema: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDITAR: ¡ahora corregido!)

Parece que hay un poco de debate con respecto a la cantidad de error y si es reparable. Me parece un mal comportamiento. Me resultó especialmente preocupante porque, al menos en Chrome, ocurre cuando el código reside en scripts que se ejecutan inmediatamente (antes de que se cargue la página), incluso cuando la consola está abierta, cada vez que se actualiza la página. Llamar a console.log cuando la consola aún no está activa solo da como resultado una referencia al objeto que está en cola, no la salida que contendrá la consola. Por lo tanto, la matriz (o cualquier objeto) no se evaluará hasta que la consola esté lista. Realmente es un caso de evaluación perezosa.

Sin embargo, hay una forma sencilla de evitar esto en su código:

var s = ["hi"]; 
console.log(s.toString()); 
s[0] = "bye"; 
console.log(s.toString()); 

Al llamar a toString, se crea una representación en memoria que no se ve alterada por declaraciones siguientes, que la consola leerá cuando esta listo. La salida de la consola es ligeramente diferente de pasar el objeto directamente, pero parece aceptable:

hi 
bye 
+1

En realidad, con matrices asociativas u otros objetos, esto podría ser un problema real, ya que toString no produce nada de valor. ¿Existe una solución fácil para los objetos en general? –

+19

JSON.stringify() – draeton

+0

@draeton: Cool. –

0

Parece que Chrome está reemplazando en su fase "de compilación pre" cualquier instancia de "s" con puntero a la matriz real .

Una forma de evitar es mediante la clonación de la matriz, la tala copia nueva en su lugar:

var s = ["hi"]; 
console.log(CloneArray(s)); 
s[0] = "bye"; 
console.log(CloneArray(s)); 

function CloneArray(array) 
{ 
    var clone = new Array(); 
    for (var i = 0; i < array.length; i++) 
     clone[clone.length] = array[i]; 
    return clone; 
} 
+0

Eso está bien, pero debido a que es una copia superficial, todavía existe la posibilidad de un problema más sutil. ¿Y qué pasa con los objetos que no son matrices? (Ese es el verdadero problema ahora.) No creo que lo que diga sobre "pre compilar" sea exacto. Además, hay un error en el código: clone [clone.length] debe clonar [i]. –

+0

No hay error, lo he ejecutado y estaba bien. clone [clone.length] es exactamente como clone [i], ya que la matriz comienza con la longitud de 0, y también lo hace el iterador de bucle "i". De todos modos, no estoy seguro de cómo se comportará con objetos complejos, pero IMO vale la pena intentarlo. Como dije, esa no es una solución, es una forma de evitar el problema. –

+0

@Shadow Wizard: Buen punto: clone.length siempre será igual a i. No funcionará para objetos. Tal vez haya una solución con "para cada uno". –

5

puede clonar una matriz con Array#slice:

console.log(s); // ["bye"], i.e. incorrect 
console.log(s.slice()); // ["hi"], i.e. correct 

Una función que se puede utilizar en lugar de console.log que no tiene este problema es el siguiente:

console.logShallowCopy = function() { 
    function slicedIfArray(arg) { 
     return Array.isArray(arg) ? arg.slice() : arg; 
    } 

    var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray); 
    return console.log.apply(console, argsSnapshot); 
}; 

Desafortunadamente, en el caso de los objetos, el mejor método parece ser depurar primero con un navegador que no sea WebKit, o escribir una función complicada para clonar. Si sólo se trabaja con objetos simples, donde el orden de las teclas no importa y no hay ninguna función, siempre se puede hacer:

console.logSanitizedCopy = function() { 
    var args = Array.prototype.slice.call(arguments); 
    var sanitizedArgs = JSON.parse(JSON.stringify(args)); 

    return console.log.apply(console, sanitizedArgs); 
}; 

Todos estos métodos son, evidentemente, muy lento, por lo que incluso más que con normal console.log s, tienes que quitarlos después de que hayas terminado de depurar.

15

De la explicación de Eric, que se debe a console.log() está en cola, y se imprime un valor posterior de la matriz (u objeto).

No puede haber soluciones: 5

1. arr.toString() // not well for [1,[2,3]] as it shows 1,2,3 
2. arr.join()  // same as above 
3. arr.slice(0)  // a new array is created, but if arr is [1, 2, arr2, 3] 
        // and arr2 changes, then later value might be shown 
4. arr.concat()  // a new array is created, but same issue as slice(0) 
5. JSON.stringify(arr) // works well as it takes a snapshot of the whole array 
         // or object, and the format shows the exact structure 
1

Esto ya está contestada, pero voy a dejar mi respuesta de todos modos. Implementé un envoltorio de consola simple que no sufre este problema. Requiere jQuery.

Se desarrolla tan sólo log, warn y error métodos, se tendrá que añadir un poco más con el fin de que sea intercambiable con un habitual console.

var fixedConsole; 
(function($) { 
    var _freezeOne = function(arg) { 
     if (typeof arg === 'object') { 
      return $.extend(true, {}, arg); 
     } else { 
      return arg; 
     } 
    }; 
    var _freezeAll = function(args) { 
     var frozen = []; 
     for (var i=0; i<args.length; i++) { 
      frozen.push(_freezeOne(args[i])); 
     } 
     return frozen; 
    }; 
    fixedConsole = { 
     log: function() { console.log.apply(console, _freezeAll(arguments)); }, 
     warn: function() { console.warn.apply(console, _freezeAll(arguments)); }, 
     error: function() { console.error.apply(console, _freezeAll(arguments)); } 
    }; 
})(jQuery); 
2

Esto ha sido parcheado en Webkit, sin embargo cuando se utiliza el marco Reaccionar esto sucede para mí en algunas circunstancias, si usted tiene este tipo de problemas sólo tiene que utilizar como otros sugieren:

console.log(JSON.stringify(the_array)); 
+1

Puede confirmar. Esto es literalmente el peor cuando intenta desconectarse de ReactSyntheticEvents. Incluso un 'JSON.parse (JSON.stringify (event))' no obtiene la profundidad/exactitud correcta. Las declaraciones de depuración son la única solución real que he encontrado para obtener la información correcta. – CStumph

Cuestiones relacionadas