La console JavaScript de Chrome est-elle paresseuse pour évaluer les tableaux?


128

Je vais commencer par le code:

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

Simple, non? En réponse à cela, Firebug dit:

["hi"]
["bye"]

Merveilleux, mais la console JavaScript de Chrome (7.0.517.41 beta) dit:

["bye"]
["bye"]

Ai-je fait quelque chose de mal, ou la console JavaScript de Chrome est-elle exceptionnellement paresseuse pour évaluer mon tableau?

entrez la description de l'image ici


1
J'observe le même comportement dans Safari - donc c'est probablement un truc webkit. Assez surprenant. J'appellerais ça un bug.
Lee

7
Pour moi, cela ressemble à un bug. Sur Linux Opera et Firefox affichent le résultat attendu, contrairement à Chrome et aux autres navigateurs Webkit. Vous voudrez peut-être signaler le problème aux développeurs Webkit: webkit.org/quality/reporting.html
tec

2
en mars 2016, ce numéro n'est plus.
kmonsoor

1
Avril 2020, ayant ce problème dans Chrome. J'ai perdu 2 heures à chercher un bogue dans mon code qui s'est avéré être un bogue dans Chrome.
The Fox le

1
Il convient également de noter que l' iinfo-bulle de l'icône bleue indique «La valeur ci-dessous a été évaluée à l'instant.».
user4642212

Réponses:


70

Merci pour le commentaire, tec. J'ai pu trouver un bogue Webkit non confirmé existant qui explique ce problème: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: maintenant corrigé!)

Il semble y avoir un débat sur la quantité de bogue et s'il peut être corrigé. Cela me semble être un mauvais comportement. C'était particulièrement troublant pour moi car, dans Chrome du moins, cela se produit lorsque le code réside dans des scripts qui sont exécutés immédiatement (avant le chargement de la page), même lorsque la console est ouverte, chaque fois que la page est actualisée. L'appel de console.log lorsque la console n'est pas encore active entraîne uniquement une référence à l'objet mis en file d'attente, et non à la sortie que la console contiendra. Par conséquent, le tableau (ou tout objet) ne sera pas évalué tant que la console ne sera pas prête. C'est vraiment un cas d'évaluation paresseuse.

Cependant, il existe un moyen simple d'éviter cela dans votre code:

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

En appelant toString, vous créez une représentation en mémoire qui ne sera pas modifiée par les instructions suivantes, que la console lira lorsqu'elle sera prête. La sortie de la console est légèrement différente du passage de l'objet directement, mais cela semble acceptable:

hi
bye

1
En fait, avec des tableaux associatifs ou d'autres objets, cela pourrait être un réel problème, car toString ne produit rien de valeur. Existe-t-il une solution de contournement facile pour les objets en général?
Eric Mickelsen

29
JSON.stringify ()
draeton

1
webkit a atterri un correctif pour cela il y a quelques mois
antony.trupe

1
faites ceci: console.log (JSON.parse (JSON.stringify (s));
Lee Comstock

Je voulais juste mentionner que dans la version actuelle de Chrome, la console est retardée et affiche à nouveau des valeurs erronées (ou a-t-elle jamais eu raison). Par exemple, j'enregistrais un tableau et j'enregistrais la valeur supérieure après l'avoir enregistré, mais il s'affichait sans la valeur sautée. Votre suggestion toString () a été très utile pour arriver là où j'avais besoin de voir les valeurs.
Nicholas R. Grant

21

D'après l'explication d'Eric, c'est dû à console.log() file d'attente, et il imprime une valeur ultérieure du tableau (ou de l'objet).

Il peut y avoir 5 solutions:

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

Vous pouvez cloner un tableau avec Array#slice:

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

Une fonction que vous pouvez utiliser à la place de console.logqui n'a pas ce problème est la suivante:

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

Pour le cas des objets, malheureusement, la meilleure méthode semble être de déboguer d'abord avec un navigateur non WebKit, ou d'écrire une fonction compliquée à cloner. Si vous travaillez uniquement avec des objets simples, où l'ordre des touches n'a pas d'importance et il n'y a pas de fonctions, vous pouvez toujours faire:

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

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

Toutes ces méthodes sont évidemment très lentes, donc plus encore qu'avec les console.logs normales , vous devez les supprimer une fois le débogage terminé.


2

Cela a été corrigé dans Webkit, mais lorsque vous utilisez le framework React, cela me arrive dans certaines circonstances, si vous rencontrez de tels problèmes, utilisez comme d'autres le suggèrent:

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

2
Peut confirmer. C'est littéralement le pire lorsque vous essayez de vous déconnecter de ReactSyntheticEvents. Même un JSON.parse(JSON.stringify(event))n'a pas la bonne profondeur / précision. Les instructions du débogueur sont la seule vraie solution que j'ai trouvée pour obtenir les informations correctes.
CStumph

1

C'est déjà répondu, mais je laisserai quand même ma réponse. J'ai implémenté un simple wrapper de console qui ne souffre pas de ce problème. Nécessite jQuery.

Il implémente uniquement log, warnet des errorméthodes, vous devrez en rajouter pour qu'il soit interchangeable avec un régulier 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

On dirait que Chrome remplace dans sa phase de "pré-compilation" toute instance de "s" par un pointeur vers le tableau réel.

Une solution consiste à cloner le tableau, en enregistrant une nouvelle copie à la place:

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

C'est bien, mais comme c'est une copie superficielle, il y a toujours la possibilité d'un problème plus subtil. Et qu'en est-il des objets qui ne sont pas des tableaux? (Voilà le vrai problème maintenant.) Je ne pense pas que ce que vous dites à propos de "pré-compilation" soit exact. De plus, il y a une erreur dans le code: clone [clone.length] doit être clone [i].
Eric Mickelsen

Aucune erreur, je l'ai exécuté et c'était OK. clone [clone.length] est exactement comme clone [i], car le tableau commence avec une longueur de 0, tout comme l'itérateur de boucle "i". Quoi qu'il en soit, je ne sais pas comment il se comportera avec des objets complexes, mais IMO, cela vaut la peine d'essayer. Comme je l'ai dit, ce n'est pas une solution, c'est un moyen de contourner le problème ..
Shadow Wizard is Ear For You

@Shadow Wizard: Bon point: clone.length sera toujours égal à i. Cela ne fonctionnera pas pour les objets. Peut-être existe-t-il une solution avec «pour chacun».
Eric Mickelsen

Objets que tu veux dire? var s = {param1: "salut", param2: "comment vas-tu?" }; si c'est le cas je viens de tester et quand vous avez s ["param1"] = "bye"; cela fonctionne bien comme prévu. Pouvez-vous s'il vous plaît poster un exemple de "cela ne fonctionnera pas pour les objets"? Je vais voir et essayer de grimper celui-là aussi.
Shadow Wizard est une oreille pour vous

@Shadow Wizard: De toute évidence, votre fonction échouera à cloner les propriétés et ne fonctionnera sur aucun objet sans propriété length. Le bogue du kit Web affecte tous les objets, pas seulement les tableaux.
Eric Mickelsen

0

la solution la plus courte à ce jour est d'utiliser une syntaxe de tableau ou de propagation d'objet pour obtenir un clone de valeurs à conserver comme au moment de la journalisation, c'est-à-dire:

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

cependant soyez averti car il fait une copie superficielle, donc toutes les valeurs non primitives imbriquées profondes ne seront pas clonées et donc affichées dans leur état modifié dans la console

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.