作者:UC 国际研发 江焘
前言
提起 console.log 方法,我们再熟悉不过了,许多初学者从第一行 console.log('Hello World') 开始学习 JavaScript(Say Hello to the world),但是有些时候 console.log 又显得有点“陌生”。
在这篇文章中,我想和你简单说说,在浏览器中 console.log 一些看起来不符合预期的表现,了解其背后的原因,以避免开发排错时不必要的困惑。
思考和观察
首先,新建 demo.html 文件,复制下方代码到文件中。
然后,以 4 种不同的步骤进行测试(所用浏览器为 Google Chrome)。
Case A
1.在 Chrome DevTools 保证关闭的状态下,使用 Chrome 浏览器打开 demo.html 文件。
2.观察控制台打印结果。
Case B
1.在 Chrome DevTools 保证打开的状态下,使用 Chrome 浏览器打开 demo.html 文件。
2.观察控制台打印结果。
Case C
1.打开 DevTools 的控制台。
2.拷贝下方代码到控制台中运行。
3.观察控制台打印结果。
Case D
1.打开 DevTools 的控制台。
2.拷贝下方代码到控制台中运行。
3.观察控制台打印结果。
Result
公布结果之前,建议大家可以先行思考一下答案,看看自己的答案和控制台结果是否一致呢?
尝试分析
不知道上面四种输出结果和你心中预期是否一致哈。
不管如何,看到 ABCD 四种结果,我们先不去看浏览器的 console.log 实现原理,来尝试分析一下:
1.面对情况 A,假设从来没有写过 console.log,猜测它是同步代码或者异步代码中的一种。那根据结果来看,我暂定 console.log 是异步执行的代码。
2.基于情况 A,再思考下情况 B。按照前面的猜测,console.log 应该是异步的,那为什么打印出来的结果看上去是浏览器会同步执行 console.log 方法呢?好吧,情况 A 初始状态是关闭 DevTools 的,而情况 B 初始状态是打开。继续猜测只有打开 DevTools 的情况下,console.log 才是同步执行。
3.根据前面的猜测,看到情况 C 觉得符合预期。
4.再看情况 D,又不符合预期了。打开 DevTools 的情况下,不是应该同步执行 console.log 的吗?再琢磨一下,打印出来是一个可以展开的对象,猜测展开它的时候,chrome 又会对它求一次值,这一次是显示它的属性。
了解原理
根据刚刚的尝试分析,我们发现一个头疼的问题:很难预测 console.log 打印出来的值会不会被 console.log 之后其他代码影响。
在某些条件下,某些浏览器的 console.log(...) 并不会把传入的内容立即输出。出现这种情况的主要原因是,在许多程序(不只是 JavaScript)中, I/O 是非常低速的阻塞部分。所以,(从页面 /UI 的角度来说)浏览器在后台异步处理控制台 I/O 能够提高性能,这时用户甚至可能根本意识不到其发生(更详细的解释可以参考《你不知道的 JavaScript(中卷)》之异步控制台)。
罗列出这四种情况,不是为了让读者去记忆各种不符合预期的情形。因为,并没有什么规范指定 console.log 方法如何执行(参阅 MDN console.log),它由宿主环境添加到 JavaScript 中。到底什么时候控制台 I/O 会延迟,甚至是否能够被观察到,这都是不确定的。因此非常不建议根据经验去判断浏览器中 console.log 的表现。
同时提一下,node 中 console.log 的表现和浏览器表现不一致,官网文档明确警告:
Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.
调试排错
开发中遇到 bug,思路清晰地去解决它是成功的第一步。
在浏览器中使用 console.log 遇到问题,了解原因,至少在下一次你想用 console.log 的时候,就可以做到心中有数。
对于经常使用 console.log 来排错的同学,可以考虑在遇到特殊情况的时候,使用更有效更快速的打断点调试方法。具体可以查阅《在 Chrome DevTools 中调试 JavaScript 入门》。