Chrome 的 JavaScript 控制台在评估对象时是否很懒惰?
- 2024-11-02 21:00:00
- admin 原创
- 49
问题描述:
我将从代码开始:
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 控制台在评估我的数组时特别懒惰?
解决方案 1:
感谢评论,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
解决方案 2:
根据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
解决方案 3:
您可以使用以下方法克隆数组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
,您必须在完成调试后将其剥离。
解决方案 4:
Webkit 已经修复了这个问题,但是在使用 React 框架时,在某些情况下会出现这种情况,如果您遇到此类问题,请按照其他人的建议使用:
console.log(JSON.stringify(the_array));
解决方案 5:
看起来 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;
}
解决方案 6:
到目前为止最短的解决方案是使用数组或对象扩展语法来获取要在记录时保存的值的克隆,即:
console.log({...myObject});
console.log([...myArray]);
但要注意,因为它执行的是浅拷贝,所以任何深层嵌套的非原始值都不会被克隆,因此在控制台中显示它们的修改状态
解决方案 7:
这个问题已经回答过了,但我还是会放弃我的答案。我实现了一个简单的控制台包装器,它不会受到这个问题的影响。需要 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);
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件