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控制台在评估数组方面异常懒惰?
感谢您的评论,技术支持。我能够找到一个可以解释此问题的现有未确认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的解释,这是由于
可以有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 |
您可以使用
1 2 | console.log(s); // ["bye"], i.e. incorrect console.log(s.slice()); // ["hi"], i.e. correct |
可以使用没有此问题的代替
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); }; |
所有这些方法显然都很慢,因此,与普通的
这已在Webkit中进行了修补,但是在使用React框架时,在某些情况下会发生这种情况,如果您遇到这样的问题,请按照其他建议使用:
1 | console.log(JSON.stringify(the_array)); |
这已经被回答了,但是无论如何我都会放弃答案。我实现了一个简单的控制台包装程序,该包装程序不会遇到此问题。需要jQuery。
它仅实现
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; } |