¿Es floja la consola JavaScript de Chrome para evaluar matrices?

126

Comenzaré con el código:

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 la consola JavaScript de Chrome está siendo extremadamente floja al evaluar mi matriz?

ingrese la descripción de la imagen aquí

Eric Mickelsen
fuente
1
Observo el mismo comportamiento en Safari, por lo que probablemente sea un kit web. Bastante sorprendente Yo lo llamaría un error.
Lee
77
A mí me parece un error. En Linux Opera y Firefox muestran el resultado esperado, Chrome y otros navegadores basados ​​en Webkit no lo hacen. Es posible que desee informar el problema a los desarrolladores de Webkit: webkit.org/quality/reporting.html
tec
2
a partir de marzo de 2016, este problema ya no existe.
kmonsoor
1
Abril de 2020, teniendo este problema en Chrome. Perdí 2 horas buscando un error en mi código que resultó ser un error en Chrome.
The Fox
1
También vale la pena señalar que la iinformación sobre herramientas del icono azul dice "El valor a continuación se evaluó en este momento".
user4642212

Respuestas:

69

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

Parece haber cierto debate sobre cuánto de un error es y si es reparable. Me parece un mal comportamiento. Fue especialmente preocupante para mí 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 el resultado 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 manera simple 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, crea una representación en la memoria que no se verá alterada por las siguientes declaraciones, que la consola leerá cuando esté lista. La salida de la consola es ligeramente diferente de pasar el objeto directamente, pero parece aceptable:

hi
bye
Eric Mickelsen
fuente
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?
Eric Mickelsen
29
JSON.stringify ()
draeton
1
webkit consiguió un parche para esto hace unos meses
antony.trupe
1
haga esto: console.log (JSON.parse (JSON.stringify (s));
Lee Comstock
Solo quería mencionar que en la versión actual de Chrome la consola se retrasa y los valores de salida vuelven a estar mal (o alguna vez estuvo bien). Por ejemplo, estaba registrando una matriz y haciendo estallar el valor superior después de registrarla, pero se mostraba sin el valor reventado. Su sugerencia toString () fue realmente útil para llegar a donde necesitaba ver los valores.
Nicholas R. Grant
21

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

Puede haber 5 soluciones:

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
nonopolaridad
fuente
7

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 puede usar en lugar de console.logque no tenga este problema es la 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);
};

Para el caso de los objetos, desafortunadamente, el mejor método parece ser depurar primero con un navegador que no sea WebKit, o escribir una función complicada para clonar. Si solo está trabajando con objetos simples, donde el orden de las teclas no importa y no hay funciones, siempre puede hacer lo siguiente:

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

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

Obviamente, todos estos métodos son muy lentos, por lo que incluso más que con los console.logs normales , debe quitarlos después de que haya terminado la depuración.

yingted
fuente
2

Esto se ha parcheado en Webkit, sin embargo, cuando uso el marco React, esto me sucede en algunas circunstancias, si tiene tales problemas, simplemente use como otros sugieren:

console.log(JSON.stringify(the_array));
justinsAccount
fuente
2
Puede confirmar. Esto es literalmente lo peor cuando se intenta cerrar la sesión ReactSyntheticEvents. Incluso a JSON.parse(JSON.stringify(event))no obtiene la profundidad / precisión correcta. Las declaraciones de depurador son la única solución real que he encontrado para obtener la información correcta.
CStumph
1

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

Solo implementa log, warny los errormétodos, tendrá que agregar un poco más para que sea intercambiable con un regular 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);
Wrygiel
fuente
0

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

Una forma de evitarlo es clonando la matriz, registrando una 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;
}
Shadow Wizard es Ear for You
fuente
Eso es bueno, pero debido a que es una copia superficial, todavía existe la posibilidad de un problema más sutil. ¿Y qué hay de los objetos que no son matrices? (Ese es el verdadero problema ahora.) No creo que lo que diga sobre "precompilación" sea exacto. Además, hay un error en el código: clone [clone.length] debería ser clone [i].
Eric Mickelsen
No hay error, lo ejecuté y estuvo bien. clone [clone.length] es exactamente como clone [i], ya que la matriz comienza con una 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 en mi opinión, vale la pena intentarlo. Como dije, esa no es una solución, es una forma de evitar el problema ...
Shadow Wizard is Ear For You el
@Shadow Wizard: Buen punto: clone.length siempre será igual a i. No funcionará para los objetos. Quizás haya una solución con "para cada uno".
Eric Mickelsen
¿A qué objetos te refieres? var s = {param1: "hola", param2: "¿cómo estás?" }; si es así, acabo de probar y cuando tienes s ["param1"] = "bye"; Está funcionando bien como se esperaba. ¿Puedes publicar un ejemplo de "no funcionará para los objetos"? Veré e intentaré escalar eso también.
Shadow Wizard es Ear For You el
@Shadow Wizard: Obviamente, su función no podrá clonar propiedades y no funcionará en ningún objeto sin una propiedad de longitud. El error del kit web afecta a todos los objetos, no solo a las matrices.
Eric Mickelsen
0

La solución más corta hasta el momento es utilizar una sintaxis de matriz o de dispersión de objetos para obtener un clon de valores que se conservarán en el momento del registro, es decir:

console.log({...myObject});
console.log([...myArray]);

Sin embargo, tenga en cuenta que se trata de una copia superficial, por lo que los valores no primitivos anidados profundos no se clonarán y, por lo tanto, se mostrarán en su estado modificado en la consola

Ptica
fuente