La console JavaScript di Chrome è pazza di valutare array?


126

Inizierò con il codice:

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

Semplice vero? In risposta a ciò, Firebug dice:

["hi"]
["bye"]

Meravigliosa, ma la console JavaScript di Chrome (7.0.517.41 beta) dice:

["bye"]
["bye"]

Ho fatto qualcosa di sbagliato o la console JavaScript di Chrome è eccezionalmente pigra nel valutare il mio array?

inserisci qui la descrizione dell'immagine


1
Osservo lo stesso comportamento in Safari, quindi probabilmente è una cosa da webkit. Abbastanza sorprendente Lo definirei un bug.
Lee,

7
A me sembra un bug. Su Linux Opera e Firefox visualizzano i risultati previsti, Chrome e altri browser basati su Webkit no. Potresti voler segnalare il problema agli sviluppatori di Webkit: webkit.org/quality/reporting.html
tec

2
a partire da marzo 2016, questo numero non è più.
kmonsoor

1
Aprile 2020, avendo questo problema in Chrome. Ho perso 2 ore a cercare un bug nel mio codice che si è rivelato essere un bug in Chrome.
La volpe il

1
Vale anche la pena notare che la idescrizione dell'icona blu dice "Il valore sotto è stato valutato proprio ora".
user4642212

Risposte:


69

Grazie per il commento, tec. Sono stato in grado di trovare un bug Webkit non confermato esistente che spiega questo problema: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: ora risolto!)

Sembra esserci un dibattito su quanto sia un bug e se sia risolvibile. Mi sembra un cattivo comportamento. Mi è stato particolarmente preoccupante perché, almeno in Chrome, si verifica quando il codice risiede negli script eseguiti immediatamente (prima che la pagina venga caricata), anche quando la console è aperta, ogni volta che la pagina viene aggiornata. Chiamare console.log quando la console non è ancora attiva comporta solo un riferimento all'oggetto in coda, non all'output che la console conterrà. Pertanto, l'array (o qualsiasi oggetto) non verrà valutato fino a quando la console non sarà pronta. È davvero un caso di valutazione pigra.

Tuttavia, esiste un modo semplice per evitarlo nel codice:

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

Chiamando toString, si crea una rappresentazione in memoria che non verrà modificata dalle seguenti istruzioni, che la console leggerà quando sarà pronta. L'output della console è leggermente diverso dal passare direttamente l'oggetto, ma sembra accettabile:

hi
bye

1
In realtà, con array associativi o altri oggetti, questo potrebbe essere un vero problema, poiché toString non produce alcun valore. Esiste una soluzione semplice per gli oggetti in generale?
Eric Mickelsen,

29
JSON.stringify ()
draeton


1
fai questo: console.log (JSON.parse (JSON.stringify (s));
Lee Comstock,

Volevo solo menzionare che nell'attuale versione di Chrome la console è in ritardo e presenta di nuovo valori errati (o è mai stato giusto). Ad esempio, stavo registrando un array e ho visualizzato il valore più alto dopo averlo registrato, ma è stato visualizzato senza il valore visualizzato. Il tuo suggerimento toString () è stato davvero utile per arrivare a dove avevo bisogno di vedere i valori.
Nicholas R. Grant,

21

Dalla spiegazione di Eric, è dovuto console.log()all'accodamento e stampa un valore successivo dell'array (o dell'oggetto).

Ci possono essere 5 soluzioni:

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

7

Puoi clonare un array con Array#slice:

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

Una funzione che è possibile utilizzare al posto di console.logquesto non presenta questo problema è la seguente:

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);
};

Nel caso degli oggetti, sfortunatamente, il metodo migliore sembra essere quello di eseguire prima il debug con un browser non WebKit o di scrivere una funzione complicata per clonare. Se lavori solo con oggetti semplici, dove l'ordine dei tasti non ha importanza e non ci sono funzioni, puoi sempre fare:

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

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

Tutti questi metodi sono ovviamente molto lenti, quindi anche più che con normali console.logs, devi toglierli dopo aver eseguito il debug.


2

Questo è stato corretto in Webkit, tuttavia quando si utilizza il framework React ciò accade in alcune circostanze, se si hanno tali problemi basta usare come altri suggeriscono:

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

2
Posso confermare Questo è letteralmente il peggio quando si tenta di disconnettersi da ReactSyntheticEvents. Anche a JSON.parse(JSON.stringify(event))non ottiene la giusta profondità / precisione. Le dichiarazioni di debugger sono l'unica vera soluzione che ho trovato per ottenere la visione corretta.
CStumph,

1

Questa è già una risposta, ma lascerò comunque la mia risposta. Ho implementato un semplice wrapper per console che non soffre di questo problema. Richiede jQuery.

Esso implementa solo log, warne errormetodi, si dovrà aggiungere un po 'di più per per essere intercambiabile con un normale 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);

0

Sembra che Chrome stia sostituendo nella sua fase di "pre-compilazione" qualsiasi istanza di "s" con un puntatore all'array reale.

Un modo per aggirare è clonare l'array, registrando invece una nuova copia:

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;
}

Va bene, ma poiché è una copia superficiale, c'è ancora la possibilità di un problema più sottile. E gli oggetti che non sono array? (Questi sono il vero problema ora.) Non penso che quello che stai dicendo su "pre-compilazione" sia accurato. Inoltre, c'è un errore nel codice: clone [clone.length] dovrebbe essere clone [i].
Eric Mickelsen,

Nessun errore, l'ho eseguito ed era OK. clone [clone.length] è esattamente come clone [i], poiché l'array inizia con una lunghezza di 0, e anche l'iteratore di loop "i". Ad ogni modo, non sono sicuro di come si comporterà con oggetti complessi, ma vale la pena provare IMO. Come ho detto, non è una soluzione, è un modo per aggirare il problema ..
Shadow Wizard è Ear For You

@Shadow Wizard: buon punto: clone.length sarà sempre uguale a i. Non funzionerà per gli oggetti. Forse esiste una soluzione con "per ciascuno".
Eric Mickelsen,

Oggetti intendi questo? var s = {param1: "ciao", param2: "come stai?" }; in tal caso ho appena provato e quando hai s ["param1"] = "bye"; funziona bene come previsto. Puoi per favore pubblicare un esempio di "non funzionerà per gli oggetti"? Vedrò e proverò a scalare anche quello.
Shadow Wizard è Ear For You il

@Shadow Wizard: Ovviamente, la tua funzione non riuscirà a clonare le proprietà e non funzionerà su nessun oggetto senza una proprietà length. Il bug del webkit interessa tutti gli oggetti, non solo le matrici.
Eric Mickelsen,

0

la soluzione più breve finora è usare la sintassi di diffusione di array o oggetti per ottenere un clone di valori da conservare come al momento della registrazione, ovvero:

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

tuttavia, ti avvisa come fa una copia superficiale, quindi qualsiasi valore non primitivo annidato in profondità non verrà clonato e quindi mostrato nel suo stato modificato nella console

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.