Chrome 的 JavaScript 控制台是否懒于评估对象?

发布于 2024-09-29 11:06:27 字数 583 浏览 7 评论 0原文

我将从代码开始:

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

很简单,对吧?对此,Firefox 控制台说:

[ "hi" ]
[ "bye" ]

太棒了,但是 Chrome 的 JavaScript 控制台(7.0.517.41 beta)说:

[ "bye" ]
[ "bye" ]

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

屏幕截图控制台表现出所描述的行为。

I’ll start with the code:

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

Simple, right? In response to this, the Firefox console says:

[ "hi" ]
[ "bye" ]

Wonderful, but Chrome’s JavaScript console (7.0.517.41 beta) says:

[ "bye" ]
[ "bye" ]

Have I done something wrong, or is Chrome’s JavaScript console being exceptionally lazy about evaluating my array?

Screenshot of the console exhibiting the described behavior.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

っ〆星空下的拥抱 2024-10-06 11:06:27

感谢您的评论,技术人员。我找到了一个现有的未经确认的 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

Thanks for the comment, tec. I was able to find an existing unconfirmed Webkit bug that explains this issue: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: now fixed!)

There appears to be some debate regarding just how much of a bug it is and whether it's fixable. It does seem like bad behavior to me. It was especially troubling to me because, in Chrome at least, it occurs when the code resides in scripts that are executed immediately (before the page is loaded), even when the console is open, whenever the page is refreshed. Calling console.log when the console is not yet active only results in a reference to the object being queued, not the output the console will contain. Therefore, the array (or any object), will not be evaluated until the console is ready. It really is a case of lazy evaluation.

However, there is a simple way to avoid this in your code:

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

By calling toString, you create a representation in memory that will not be altered by following statements, which the console will read when it is ready. The console output is slightly different from passing the object directly, but it seems acceptable:

hi
bye
卷耳 2024-10-06 11:06:27

根据 Eric 的解释,这是由于 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

From Eric's explanation, it is due to console.log() being queued up, and it prints a later value of the array (or object).

There can be 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
柳若烟 2024-10-06 11:06:27

您可以使用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.log 更慢s,你必须在完成调试后将它们删除。

You can clone an array with Array#slice:

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

A function that you can use instead of console.log that doesn't have this problem is as follows:

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

For the case of objects, unfortunately, the best method appears to be to debug first with a non-WebKit browser, or to write a complicated function to clone. If you are only working with simple objects, where order of keys doesn't matter and there are no functions, you could always do:

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

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

All of these methods are obviously very slow, so even more so than with normal console.logs, you have to strip them off after you're done debugging.

自我难过 2024-10-06 11:06:27

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

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

This has been patched in Webkit, however when using the React framework this happens for me in some circumstances, if you have such problems just use as others suggest:

console.log(JSON.stringify(the_array));
同尘 2024-10-06 11:06:27

看起来 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;
}

Looks like Chrome is replacing in its "pre compile" phase any instance of "s" with pointer to the actual array.

One way around is by cloning the array, logging fresh copy instead:

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;
}
眼泪也成诗 2024-10-06 11:06:27

到目前为止,最短的解决方案是使用数组或对象扩展语法来获取要在记录时保留的值的克隆,即:

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

但是请注意,因为它执行浅复制,因此任何深层嵌套的非原始值都不会被克隆并因此在控制台中以修改后的状态显示

the shortest solution so far is to use array or object spread syntax to get a clone of values to be preserved as in time of logging, ie:

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

however be warned as it does a shallow copy, so any deep nested non-primitive values will not be cloned and thus shown in their modified state in the console

森林散布 2024-10-06 11:06:27

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

它仅实现 logwarnerror 方法,您必须添加更多方法才能使其与常规 互换>控制台

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

This is already answered, but I'll drop my answer anyway. I implemented a simple console wrapper which doesn't suffer from this issue. Requires jQuery.

It implements only log, warn and error methods, you will have to add some more in order for it to be interchangeable with a 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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文