Ist die JavaScript-Konsole von Chrome beim Auswerten von Arrays faul?

126

Ich werde mit dem Code beginnen:

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

Einfach, richtig? Als Antwort darauf sagt Firebug:

["hi"]
["bye"]

Wunderbar, aber die JavaScript-Konsole von Chrome (7.0.517.41 Beta) sagt:

["bye"]
["bye"]

Habe ich etwas falsch gemacht oder ist die JavaScript-Konsole von Chrome bei der Bewertung meines Arrays außergewöhnlich faul?

Geben Sie hier die Bildbeschreibung ein

Eric Mickelsen
quelle
1
Ich beobachte das gleiche Verhalten in Safari - es ist also wahrscheinlich eine Webkit-Sache. Ziemlich überraschend. Ich würde es einen Fehler nennen.
Lee
@mplungjan - das stimmt nicht. Die erste Zeile deklariert ein "einfaches altes" Array mit einem einzelnen Element am Index 0. Die dritte Zeile weist diesem Element einfach einen neuen Wert zu. Beide Fälle arbeiten mit einem einfachen numerisch indizierten Array.
Lee
Wenn dies ein Fehler ist, ist es für mich unverständlich, warum dieser Fehler nicht gefunden und behoben wurde.
Unpolarität
7
Für mich sieht es aus wie ein Käfer. Unter Linux Opera und Firefox wird das erwartete Ergebnis angezeigt, Chrome und andere Webkit-basierte Browser nicht. Vielleicht möchten Sie das Problem den Webkit-Entwicklern melden: webkit.org/quality/reporting.html
tec
2
Ab März 2016 ist diese Ausgabe nicht mehr verfügbar.
kmonsoor

Antworten:

69

Danke für den Kommentar, tec. Ich konnte einen vorhandenen unbestätigten Webkit-Fehler finden, der dieses Problem erklärt: https://bugs.webkit.org/show_bug.cgi?id=35801 (BEARBEITEN: jetzt behoben!)

Es scheint eine Debatte darüber zu geben, wie groß der Fehler ist und ob er behoben werden kann. Es scheint mir ein schlechtes Benehmen zu sein. Es hat mich besonders beunruhigt, weil es zumindest in Chrome auftritt, wenn sich der Code in Skripten befindet, die unmittelbar (vor dem Laden der Seite) ausgeführt werden, selbst wenn die Konsole geöffnet ist, wenn die Seite aktualisiert wird. Das Aufrufen von console.log, wenn die Konsole noch nicht aktiv ist, führt nur zu einem Verweis auf das Objekt in der Warteschlange, nicht auf die Ausgabe, die die Konsole enthält. Daher wird das Array (oder ein beliebiges Objekt) erst ausgewertet, wenn die Konsole bereit ist. Es ist wirklich ein Fall von fauler Bewertung.

Es gibt jedoch eine einfache Möglichkeit, dies in Ihrem Code zu vermeiden:

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

Durch Aufrufen von toString erstellen Sie eine Darstellung im Speicher, die nicht durch folgende Anweisungen geändert wird, die die Konsole liest, wenn sie bereit ist. Die Konsolenausgabe unterscheidet sich geringfügig von der direkten Übergabe des Objekts, scheint jedoch akzeptabel zu sein:

hi
bye
Eric Mickelsen
quelle
1
Bei assoziativen Arrays oder anderen Objekten kann dies ein echtes Problem sein, da toString nichts Wertvolles erzeugt. Gibt es eine einfache Umgehung für Objekte im Allgemeinen?
Eric Mickelsen
29
JSON.stringify ()
draeton
1
Webkit hat vor ein paar Monaten einen Patch dafür gelandet
antony.trupe
1
Tun Sie dies: console.log (JSON.parse (JSON.stringify (s));
Lee Comstock
Ich wollte nur erwähnen, dass in der aktuellen Chrome-Version die Konsole verzögert ist und wieder falsche Werte ausgibt (oder war es jemals richtig). Zum Beispiel habe ich ein Array protokolliert und den höchsten Wert nach dem Protokollieren angezeigt, aber es wurde ohne den angezeigten Wert angezeigt. Ihr toString () -Vorschlag war wirklich hilfreich, um dorthin zu gelangen, wo ich die Werte sehen musste.
Nicholas R. Grant
21

Nach Erics Erklärung liegt es daran, dass es sich in console.log()der Warteschlange befindet und einen späteren Wert des Arrays (oder Objekts) druckt.

Es kann 5 Lösungen geben:

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
Unpolarität
quelle
7

Sie können ein Array klonen mit Array#slice:

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

Eine Funktion, die Sie stattdessen verwenden können console.log, hat dieses Problem nicht:

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

Für Objekte scheint die beste Methode leider darin zu bestehen, zuerst mit einem Nicht-WebKit-Browser zu debuggen oder eine komplizierte Funktion zum Klonen zu schreiben. Wenn Sie nur mit einfachen Objekten arbeiten, bei denen die Reihenfolge der Tasten keine Rolle spielt und keine Funktionen vorhanden sind, können Sie immer Folgendes tun:

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

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

Alle diese Methoden sind offensichtlich sehr langsam. Noch mehr als bei normalen console.logs müssen Sie sie nach dem Debuggen entfernen.

yingted
quelle
2

Dies wurde in Webkit gepatcht. Wenn Sie jedoch das React-Framework verwenden, geschieht dies unter bestimmten Umständen für mich, wenn Sie solche Probleme haben, verwenden Sie sie einfach so, wie andere vorschlagen:

console.log(JSON.stringify(the_array));
justinsAccount
quelle
2
Kann bestätigen. Dies ist buchstäblich das Schlimmste, wenn Sie versuchen, ReactSyntheticEvents abzumelden. Auch ein JSON.parse(JSON.stringify(event))bekommt nicht die richtige Tiefe / Genauigkeit. Debugger-Anweisungen sind die einzige echte Lösung, die ich gefunden habe, um den richtigen Einblick zu erhalten.
CStumph
1

Dies ist bereits beantwortet, aber ich werde meine Antwort trotzdem fallen lassen. Ich habe einen einfachen Konsolen-Wrapper implementiert, der unter diesem Problem nicht leidet. Benötigt jQuery.

Es werden nur Methoden implementiert log, warnund errorSie müssen weitere hinzufügen, damit es mit einem regulären austauschbar ist 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
quelle
0

Es sieht so aus, als würde Chrome in seiner "Pre Compile" -Phase jede Instanz von "s" durch einen Zeiger auf das tatsächliche Array ersetzen .

Eine Möglichkeit besteht darin, das Array zu klonen und stattdessen eine neue Kopie zu protokollieren:

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;
}
Der Schatten-Assistent ist das Ohr für Sie
quelle
Das ist gut, aber da es sich um eine flache Kopie handelt, besteht immer noch die Möglichkeit eines subtileren Problems. Und was ist mit Objekten, die keine Arrays sind? (Das ist jetzt das eigentliche Problem.) Ich denke nicht, dass das, was Sie über "Pre Compile" sagen, korrekt ist. Außerdem gibt es einen Fehler im Code: clone [clone.length] sollte clone [i] sein.
Eric Mickelsen
Kein Fehler, ich habe es ausgeführt und es war OK. clone [clone.length] ist genau wie clone [i], da das Array mit der Länge 0 beginnt, ebenso wie der Schleifeniterator "i". Wie auch immer, ich bin mir nicht sicher, wie es sich mit komplexen Objekten verhalten wird, aber IMO ist es einen Versuch wert. Wie ich schon sagte, das ist keine Lösung, es ist ein Weg, um das Problem zu umgehen.
Shadow Wizard ist Ear For You
@ Shadow Wizard: Guter Punkt: clone.length ist immer gleich i. Es wird nicht für Objekte funktionieren. Vielleicht gibt es eine Lösung mit "für jeden".
Eric Mickelsen
Objekte meinst du das? var s = {param1: "hi", param2: "wie geht es dir?" }; wenn ja habe ich gerade getestet und wenn du s ["param1"] = "bye" hast; es funktioniert wie erwartet gut. Können Sie bitte ein Beispiel für "Es funktioniert nicht für Objekte" posten? Ich werde sehen und versuchen, auch diesen zu besteigen.
Schatten-Assistent ist Ohr für Sie
@ Shadow-Assistent: Offensichtlich kann Ihre Funktion keine Eigenschaften klonen und funktioniert nicht mit Objekten ohne Längeneigenschaft. Der Webkit-Fehler betrifft alle Objekte, nicht nur Arrays.
Eric Mickelsen
0

Die bisher kürzeste Lösung besteht darin, die Array- oder Objektverbreitungssyntax zu verwenden, um einen Klon von Werten zu erhalten, die zum Zeitpunkt der Protokollierung beibehalten werden sollen, dh:

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

Seien Sie jedoch gewarnt, da es sich um eine flache Kopie handelt, sodass tief verschachtelte nicht-primitive Werte nicht geklont und somit in ihrem geänderten Zustand in der Konsole angezeigt werden

ptica
quelle