今天在工作中遇到的,使用console.log()输出 对象 信息时,出现输出的信息跟自己想的不一样的问题,着实让人诧异。便去查了一下,终于知道了原因,这里留作记录
参考https://github.com/Mmzer/think/issues/30
问题示例:
执行如下代码
let obj={age:20}
console.log(obj)
obj.age=30
得到结果
可以看到,不展开obj对象,看到的是我们预期的20,展开对象,看到的却是30。如果我们输出一个字段很多的对象,那必须展开才能看到属性值,否则就会被非预期的属性值误导。
。
。
。
。
。
原因分析:
- 不展开对象看时,console.log()是按照代码执行顺序,同步地输出了对象当时的快照。所以我们看到的是预期的值。
- 展开对象时,它其实是重新去内存中读取对象的属性值,此时对象属性已被更改,所以展开对象后,可能看到的不是预期值了。
。
。
。
。
。
浏览器或者开发者工具(F12)为什么出现这种情况?
这个问题,在《你不知道的javascript中卷》第二部分异步和性能1.1节一部控制台部分有提及:
并没有什么规范或一组需求指定console.* 方法族如何工作——它们并不是JavaScript 正式的一部分,而是由宿主环境(请参考本书的“类型和语法”部分)添加到JavaScript 中的。
因此,不同的浏览器和JavaScript 环境可以按照自己的意愿来实现,有时候这会引起混淆。
尤其要提出的是,在某些条件下,某些浏览器的console.log(..) 并不会把传入的内容立即输出。
出现这种情况的主要原因是,在许多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。
所以,(从页面/UI 的角度来说)浏览器在后台异步处理控制台I/O 能够提高性能,这时用户甚至可能根本意识不到其发生。
。
。
。
。
。
总结
- console.log()打印出来的内容并不是一定百分百可信的内容。一般对于基本类型number、string、boolean、null、undefined的输出是可信的。但对于Object等引用类型来说,则就会出现上述异常打印输出。
- 尽量不要直接输出对象,先将对象序列化JSON.stringify()为字符串再输出
- 最好使用打断点(debugger)的方式来调试。