感谢你的评论,tec。我能够找到一个现有的未经确认的Webkit bug来解释这个问题:https://bugs.webkit.org/show_bug.cgi?id=35801(编辑:现在已经修复!)
关于这个bug有一些争议,就它到底算不算bug、是不是可以修复等等问题。对我来说,这似乎是一种不好的行为。特别是在Chrome中,即使控制台打开时,当代码存在于立即执行的脚本中并且页面重新加载时,它也会发生。当控制台尚未激活时调用console.log仅导致将对象引用排队,而不是控制台将包含的输出。因此,数组(或任何对象)直到控制台准备好才被评估。这真的是一种惰性评估的情况。
但是,在您的代码中有一种简单的方法可以避免这种情况:
var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());
调用toString方法,可以创建一个在接下来的语句中不会改变的内存表示,在控制台准备好时将被读取。 控制台输出与直接传递对象略有不同,但似乎可以接受:
hi
bye
debugger;
来设置断点也是一个很好的选择(如果可行的话,也可以手动从开发者工具中添加断点)。 - Giorgio Tempesta根据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
copy = {...orig}
。 - ScarArray#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
时,你也必须在完成调试后将它们删除。console.log(JSON.stringify(the_array));
JSON.parse(JSON.stringify(event))
也无法获得正确的深度/准确性。调试器语句是我找到的唯一真正解决此问题的方法。 - CStumph看起来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;
}
slice
。 - Shadow The Spring Wizard目前最简单的解决方案是使用数组或对象扩展语法来获取需要保留的值的克隆,以便在记录时保留其状态,即:
console.log({...myObject});
console.log([...myArray]);
但是需要注意的是,它只进行浅复制,因此任何深度嵌套的非原始值都不会被克隆,因此在控制台中显示它们的修改状态。
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);