O console JavaScript do Chrome é preguiçoso ao avaliar matrizes?


126

Vou começar com o código:

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

Simples, certo? Em resposta a isso, o Firebug diz:

["hi"]
["bye"]

Maravilhoso, mas o console JavaScript do Chrome (7.0.517.41 beta) diz:

["bye"]
["bye"]

Fiz algo errado ou o console JavaScript do Chrome é excepcionalmente preguiçoso ao avaliar minha matriz?

insira a descrição da imagem aqui


1
Eu observo o mesmo comportamento no Safari - então provavelmente é uma coisa do kit da web. Bastante surpreendente. Eu chamaria isso de bug.
Lee

7
Para mim, parece um bug. No Linux Opera e Firefox, exibem o resultado esperado, o Chrome e outros navegadores baseados no Webkit não. Convém relatar o problema aos desenvolvedores do Webkit: webkit.org/quality/reporting.html
tec

2
em março de 2016, esse problema não existe mais.
kmonsoor 31/03

1
Abril de 2020, com esse problema no Chrome. Passei duas horas procurando um bug no meu código que acabou sendo um bug no Chrome.
The Fox

1
Também é importante notar que a idica do ícone azul diz "O valor abaixo foi avaliado agora".
user4642212 13/07

Respostas:


69

Obrigado pelo comentário, tec. Consegui encontrar um bug do Webkit não confirmado existente que explica esse problema: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: agora corrigido!)

Parece haver algum debate sobre o quanto de um bug é e se é corrigível. Parece um mau comportamento para mim. Foi especialmente preocupante para mim porque, pelo menos no Chrome, ocorre quando o código reside em scripts executados imediatamente (antes do carregamento da página), mesmo quando o console é aberto, sempre que a página é atualizada. Chamar console.log quando o console ainda não está ativo resulta apenas em uma referência ao objeto na fila, não na saída que o console conterá. Portanto, a matriz (ou qualquer objeto) não será avaliada até que o console esteja pronto. É realmente um caso de avaliação preguiçosa.

No entanto, existe uma maneira simples de evitar isso em seu código:

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

Ao chamar toString, você cria uma representação na memória que não será alterada pelas instruções a seguir, que o console lerá quando estiver pronto. A saída do console é um pouco diferente de passar o objeto diretamente, mas parece aceitável:

hi
bye

1
Na verdade, com matrizes associativas ou outros objetos, isso pode ser um problema real, pois toString não produz nada de valor. Existe uma solução fácil para objetos em geral?
Eric Mickelsen 30/10/10

29
JSON.stringify ()
draeton

1
webkit conseguiu um patch para essa há alguns meses
antony.trupe

1
fazer isso: console.log (JSON.parse (JSON.stringify (s));
Lee Comstock

Eu só queria mencionar que na versão atual do Chrome o console está atrasado e os valores de saída estão errados novamente (ou estava certo). Por exemplo, eu estava registrando uma matriz e exibindo o valor mais alto depois de registrá-la, mas estava aparecendo sem o valor exibido. Sua sugestão toString () foi realmente útil para chegar onde eu precisava ver os valores.
Nicholas R. Grant

21

Pela explicação de Eric, é devido à console.log()fila e imprime um valor posterior da matriz (ou objeto).

Pode haver 5 soluções:

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

Você pode clonar uma matriz com Array#slice:

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

Uma função que você pode usar em vez de console.lognão ter esse problema é a seguinte:

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

Infelizmente, para o caso de objetos, o melhor método parece ser depurar primeiro com um navegador que não seja o WebKit ou escrever uma função complicada para clonar. Se você estiver trabalhando apenas com objetos simples, onde a ordem das chaves não importa e não há funções, você sempre pode:

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

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

Todos esses métodos são obviamente muito lentos; portanto, mais do que com os console.logs normais , você precisa removê-los depois de concluir a depuração.


2

Isso foi corrigido no Webkit, no entanto, ao usar a estrutura React, isso acontece comigo em algumas circunstâncias, se você tiver esses problemas, use o que outros sugerem:

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

2
Pode confirmar. Isso é literalmente o pior ao tentar sair do ReactSyntheticEvents. Mesmo um JSON.parse(JSON.stringify(event))não obtém a profundidade / precisão corretas. As instruções do depurador são a única solução real que encontrei para obter o insight correto.
CStumph

1

Isso já foi respondido, mas deixarei minha resposta de qualquer maneira. Eu implementei um wrapper de console simples que não sofre com esse problema. Requer jQuery.

Ele implementa apenas log, warne errormétodos, você precisará adicionar um pouco mais para que seja intercambiável com um 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);

0

Parece que o Chrome está substituindo na fase "pré-compilação" qualquer instância de "s" por ponteiro para a matriz real.

Uma maneira de contornar é clonar a matriz, registrando uma nova cópia:

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

Isso é bom, mas como é uma cópia superficial, ainda existe a possibilidade de um problema mais sutil. E quanto aos objetos que não são matrizes? (Esse é o verdadeiro problema agora.) Não acho que o que você está dizendo sobre "pré-compilação" seja preciso. Além disso, há um erro no código: clone [clone.length] deve ser clone [i].
Eric Mickelsen 31/10/10

Sem erro, eu o executei e estava tudo bem. clone [clone.length] é exatamente igual ao clone [i], pois a matriz começa com comprimento 0 e o iterador de loop "i". Enfim, não sei como ele se comportará com objetos complexos, mas na IMO vale a pena tentar. Como eu disse, isso não é uma solução, é uma maneira de contornar o problema ..
Shadow Wizard é Ear For You

@ Shadow Wizard: Bom ponto: clone.length sempre será igual a i. Não funcionará para objetos. Talvez haja uma solução com "para cada".
Eric Mickelsen

Objetos você quer dizer isso? var s = {param1: "oi", param2: "como você está?" }; se sim, acabei de testar e quando você tem s ["param1"] = "tchau"; está funcionando bem como esperado. Você pode postar um exemplo de "não funcionará para objetos"? Vou ver e tentar escalar esse também.
Shadow Wizard é Ear For You

@Shadow Wizard: Obviamente, sua função falhará ao clonar propriedades e não funcionará em nenhum objeto sem uma propriedade length. O bug do kit da web afeta todos os objetos, não apenas as matrizes.
Eric Mickelsen

0

a solução mais curta até agora é usar a sintaxe de dispersão de matriz ou objeto para obter um clone de valores a serem preservados como no momento do registro, ou seja:

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

no entanto, seja avisado como faz uma cópia superficial, para que quaisquer valores não primitivos aninhados profundos não sejam clonados e mostrados em seu estado modificado no console

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.