Chrome的JavaScript控制台是否懒于评估数组?

Is Chrome's JavaScript console lazy about evaluating arrays?

我将从代码开始:

1
2
3
4
var s = ["hi"];
console.log(s);
s[0] ="bye";
console.log(s);

简单吧? 对此,Firebug说:

1
2
["hi"]
["bye"]

很棒,但是Chrome的JavaScript控制台(7.0.517.41 Beta)说:

1
2
["bye"]
["bye"]

我做错了什么吗?还是Chrome的JavaScript控制台在评估数组方面异常懒惰?

enter image description here


感谢您的评论,技术支持。我能够找到一个可以解释此问题的现有未确认Webkit错误:https://bugs.webkit.org/show_bug.cgi?id=35801(编辑:现已修复!)

关于该漏洞的数量及其是否可修复似乎存在一些争论。在我看来,这似乎是不良行为。这尤其令我感到困扰,因为至少在Chrome中,当代码驻留在立即(在加载页面之前)执行的脚本中时(即使在控制台打开时,只要刷新页面),就会发生这种情况。在控制台尚未激活时调用console.log只会导致对排队对象的引用,而不是控制台将包含的输出。因此,在准备好控制台之前,不会评估数组(或任何对象)。这确实是一个懒惰评估的情况。

但是,有一种简单的方法可以在您的代码中避免这种情况:

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

通过调用toString,您可以在内存中创建一个表示形式,该表示形式不会被以下语句更改,控制台准备就绪后,控制台将读取该语句。控制台输出与直接传递对象略有不同,但似乎可以接受:

1
2
hi
bye


根据Eric的解释,这是由于console.log()被排队,并且它打印了数组(或对象)的更高值。

可以有5个解决方案:

1
2
3
4
5
6
7
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


您可以使用Array#slice克隆数组:

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

可以使用没有此问题的代替console.log的函数如下:

1
2
3
4
5
6
7
8
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浏览器进行调试,或者编写一个复杂的函数进行克隆。如果您只使用简单的对象,而键的顺序无关紧要且没有功能,则可以始终这样做:

1
2
3
4
5
6
console.logSanitizedCopy = function () {
    var args = Array.prototype.slice.call(arguments);
    var sanitizedArgs = JSON.parse(JSON.stringify(args));

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

所有这些方法显然都很慢,因此,与普通的console.log相比,它甚至更是如此,您必须在完成调试后将其剥离。


这已在Webkit中进行了修补,但是在使用React框架时,在某些情况下会发生这种情况,如果您遇到这样的问题,请按照其他建议使用:

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


这已经被回答了,但是无论如何我都会放弃答案。我实现了一个简单的控制台包装程序,该包装程序不会遇到此问题。需要jQuery。

它仅实现logwarnerror方法,您将不得不添加更多内容以便使其与常规console互换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);

看起来Chrome浏览器正在其"预编译"阶段用指向实际数组的指针替换" s"的任何实例。

一种解决方法是克隆阵列,而是记录新副本:

1
2
3
4
5
6
7
8
9
10
11
12
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;
}