Консоль JavaScript в Chrome ленив при оценке массивов?

Начну с кода:

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

Все просто, правда? В ответ на это Firebug говорит:

["hi"]
["bye"]

Замечательно, но консоль JavaScript Chrome (бета-версия 7.0.517.41) говорит:

["bye"]
["bye"]

Я сделал что-то не так, или консоль JavaScript Chrome исключительно ленива при оценке моего массива?

введите описание изображения здесь


person Eric Mickelsen    schedule 30.10.2010    source источник
comment
Я наблюдаю такое же поведение в Safari - так что это, вероятно, вещь webkit. Довольно удивительно. Я бы назвал это ошибкой.   -  person Lee    schedule 30.10.2010
comment
@mplungjan - это неправда. первая строка объявляет простой старый массив с единственным элементом с индексом 0. Третья строка просто присваивает новое значение этому элементу. оба случая работают с простым массивом с числовыми индексами.   -  person Lee    schedule 30.10.2010
comment
Если это ошибка, я не понимаю, почему эта ошибка не была обнаружена и исправлена.   -  person nonopolarity    schedule 30.10.2010
comment
Для меня это похоже на ошибку. В Linux Opera и Firefox отображают ожидаемый результат, а в Chrome и других браузерах на основе Webkit - нет. Вы можете сообщить о проблеме разработчикам Webkit: webkit.org/quality/reporting.html   -  person tec    schedule 30.10.2010
comment
DOH - Вы, конечно, правы. Я не полностью проснулся   -  person mplungjan    schedule 30.10.2010
comment
Я обнаружил ту же проблему с Firebug для Firefox. Действительно разочаровывает. Я подозревал, что функция перемешивания ведет себя странно, пока я не решил проверить с помощью jsbin и не использовал .toString(). Вот код jsbin. В части счетчика консоли, начиная со строки 8 и далее, исходный массив также выглядит перетасованным.   -  person Majid Fouladpour    schedule 09.08.2013
comment
Черт, это сводило меня с ума.   -  person Ben Liyanage    schedule 28.02.2015
comment
См. Также console.log () async or sync? для общего объяснения.   -  person Bergi    schedule 06.08.2015
comment
по состоянию на март 2016 года этого выпуска больше нет.   -  person kmonsoor    schedule 31.03.2016
comment
Апрель 2020 г., возникла эта проблема в Chrome. Потратил 2 часа на поиск ошибки в моем коде, которая оказалась ошибкой в ​​Chrome.   -  person The Fox    schedule 25.04.2020
comment
Также стоит отметить, что во всплывающей подсказке синего значка i написано: «Значение, указанное ниже, было оценено только что».   -  person Sebastian Simon    schedule 13.07.2020
comment
Я решил свой, удалив метод setTimeout   -  person Edison Pebojot    schedule 24.03.2021


Ответы (7)


Спасибо за комментарий, tec. Мне удалось найти существующую неподтвержденную ошибку Webkit, которая объясняет эту проблему: https://bugs.webkit.org/show_bug.cgi?id=35801 (РЕДАКТИРОВАТЬ: теперь исправлено!)

Похоже, есть некоторые споры относительно того, насколько это ошибка и можно ли ее исправить. Мне это действительно кажется плохим поведением. Меня это особенно беспокоило, потому что, по крайней мере, в Chrome, это происходит, когда код находится в сценариях, которые выполняются немедленно (до загрузки страницы), даже когда консоль открыта, всякий раз, когда страница обновляется. Вызов console.log, когда консоль еще не активна, приводит только к ссылке на объект, помещенный в очередь, но не к выходным данным, которые консоль будет содержать. Следовательно, массив (или любой объект) не будет оцениваться, пока консоль не будет готова. Это действительно случай ленивых вычислений.

Однако есть простой способ избежать этого в вашем коде:

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

Вызывая toString, вы создаете представление в памяти, которое не будет изменено следующими операторами, которые консоль прочитает, когда оно будет готово. Вывод консоли немного отличается от передачи объекта напрямую, но кажется приемлемым:

hi
bye
person Eric Mickelsen    schedule 30.10.2010
comment
На самом деле, с ассоциативными массивами или другими объектами это может быть реальной проблемой, поскольку toString не производит ничего ценного. Есть ли простой способ обхода объектов в целом? - person Eric Mickelsen; 30.10.2010
comment
webkit установил патч для этого несколько месяцев назад - person antony.trupe; 09.10.2012
comment
сделайте это: console.log (JSON.parse (JSON.stringify (s)); - person Lee Comstock; 11.04.2018
comment
Я просто хотел упомянуть, что в текущей версии Chrome консоль задерживается и снова выводит неверные значения (или это было когда-либо правильно). Например, я регистрировал массив и выскакивал верхнее значение после регистрации, но он отображался без всплывающего значения. Ваше предложение toString () действительно помогло мне добраться до того места, где мне нужно было увидеть значения. - person Nicholas R. Grant; 06.12.2018
comment
Вставка точки останова из кода с debugger; также является отличным вариантом. (Или вручную добавьте точку останова из инструментов разработчика, если это возможно). - person Giorgio Tempesta; 31.01.2020

По объяснению Эрика, это связано с тем, что console.log() находится в очереди, и он печатает более позднее значение массива (или объекта).

Возможных решений 5:

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
person nonopolarity    schedule 07.10.2012

Вы можете клонировать массив с помощью Array#slice:

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

Функция, которую вы можете использовать вместо console.log, у которой нет этой проблемы, выглядит следующим образом:

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

В случае с объектами, к сожалению, лучшим методом является сначала отладка в браузере, отличном от WebKit, или создание сложной функции для клонирования. Если вы работаете только с простыми объектами, где порядок клавиш не имеет значения и нет функций, вы всегда можете сделать:

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

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

Очевидно, что все эти методы очень медленные, поэтому даже больше, чем с обычными console.logs, вы должны удалить их после того, как закончите отладку.

person yingted    schedule 27.01.2011

Это было исправлено в Webkit, однако при использовании фреймворка React это происходит для меня в некоторых случаях, если у вас есть такие проблемы, просто используйте, как предлагают другие:

console.log(JSON.stringify(the_array));
person justinsAccount    schedule 30.04.2015
comment
Могу подтвердить. Это буквально худшее при попытке выйти из ReactSyntheticEvents. Даже JSON.parse(JSON.stringify(event)) не дает нужной глубины / точности. Операторы отладчика - единственное реальное решение, которое я нашел, чтобы получить правильное представление. - person CStumph; 28.07.2015

На это уже есть ответ, но я все равно оставлю свой ответ. Я реализовал простую консольную оболочку, которая не страдает от этой проблемы. Требуется jQuery.

Он реализует только методы log, warn и error, вам нужно будет добавить еще несколько, чтобы он мог быть взаимозаменяемым с обычным 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);
person wrygiel    schedule 28.02.2013

на данный момент самым коротким решением является использование синтаксиса распределения массива или объекта, чтобы получить клон значений, которые будут сохранены во время регистрации, то есть:

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

однако имейте в виду, что это неглубокая копия, поэтому любые глубоко вложенные непримитивные значения не будут клонированы и, таким образом, отображаться в их измененном состоянии в консоли

person ptica    schedule 12.04.2020

Похоже, Chrome на этапе «предварительной компиляции» заменяет любой экземпляр «s» на указатель на фактический массив.

Один из способов - клонировать массив, вместо этого регистрируя новую копию:

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;
}
person Shadow Wizard Wearing Mask V2    schedule 31.10.2010
comment
Это хорошо, но поскольку это неглубокая копия, все же существует возможность возникновения более тонкой проблемы. А как насчет объектов, не являющихся массивами? (Сейчас это настоящая проблема.) Я не думаю, что то, что вы говорите о предварительной компиляции, является правильным. Также в коде есть ошибка: clone [clone.length] должен быть clone [i]. - person Eric Mickelsen; 31.10.2010
comment
Никакой ошибки, я выполнил его, и все было в порядке. clone [clone.length] точно так же, как clone [i], так как массив начинается с длины 0, как и итератор цикла i. В любом случае, не уверен, как он будет вести себя со сложными объектами, но, IMO, стоит попробовать. Как я уже сказал, это не решение, это способ обойти проблему ... - person Shadow Wizard Wearing Mask V2; 01.11.2010
comment
@Shadow Wizard: Хороший момент: clone.length всегда будет равно i. Для объектов это не сработает. Возможно, для каждого найдется решение. - person Eric Mickelsen; 01.11.2010
comment
Объекты ты это имеешь ввиду? var s = {param1: hi, param2: как дела? }; если да, то я только что проверил, и когда у вас s [param1] = bye; он работает нормально, как и ожидалось. Не могли бы вы опубликовать пример того, что он не работает для объектов? Я посмотрю и попробую залезть и на эту. - person Shadow Wizard Wearing Mask V2; 02.11.2010
comment
@Shadow Wizard: очевидно, ваша функция не сможет клонировать свойства и не будет работать с любыми объектами без свойства длины. Ошибка webkit затрагивает все объекты, а не только массивы. - person Eric Mickelsen; 03.11.2010
comment
@Domenic Потому что я тогда не был знаком с slice. - person Shadow Wizard Wearing Mask V2; 01.07.2011
comment
@Shadow Wizard Достаточно справедливо :). Я взял ответ Anonymous с решением на основе slice. - person Domenic; 01.07.2011
comment
@Dom, почему тогда не новый ответ? :) - person Shadow Wizard Wearing Mask V2; 01.07.2011
comment
Ну, это уже было slice, но потребовались некоторые исправления ESL и ясности. Основное содержание остается у мистера Таинственного Анонима, поэтому я подумал, что вместо этого внесу правку. - person Domenic; 01.07.2011