不要被 console.log 欺骗了

·  阅读 1171

一个例子

var a = {
  index: 1
}
console.log(a);
a.index++;
复制代码

我们可能会认为打印的结果是 {a: 1},但是实际情况如下: image.png

可以看到打印出来的确实是{a: 1},但是将其展开之后显示的index的值为2,这是为什么呢?这时我们发现结果中有个叹号,我们把鼠标移上去看看是什么? image.png 意思就是这个值计算了一次,但是后面可能是会变化的,那么为什么会出现这种情况呢?其实在《你不知道的JavaScript》这本书的中卷中给出了答案:

并没有什么规范或一组需求指定 console.* 方法族如何工作——它们并不是JavaScript 正式的一部分,而是由宿主环境(请参考本书的“类型和语法”部分)添加到JavaScript 中的。因此,不同的浏览器和JavaScript 环境可以按照自己的意愿来实现,有时候这会引起混淆。

尤其要提出的是,在某些条件下,某些浏览器的 console.log(…) 并不会把传入的内容立即输出。出现这种情况的主要原因是,在许多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(从页面/UI 的角度来说)浏览器在后台异步处理控制台I/O 能够提高性能,这时用户甚至可能根本意识不到其发生。

我们通常认为恰好在执行到console.log(..) 语句的时候会看到a对象的快照,打印出类似于{ index: 1 } 这样的内容,然后在下一条语句a.index++ 执行时将其修改,这句的执行会严格在a 的输出之后。多数情况下,前述代码在开发者工具的控制台中输出的对象表示与期望是一致的。但是,这段代码运行的时候,浏览器可能会认为需要把控制台I/O 延迟到后台,在这种情况下,等到浏览器控制台输出对象内容时,a.index++ 可能已经执行,因此会显示{ index: 2 }

到底什么时候控制台I/O 会延迟,甚至是否能够被观察到,这都是游移不定的。所以如果在调试的过程中遇到对象在console.log(..) 语句之后被修改,可你却看到了意料之外的结果,要意识到这可能是这种I/O 的异步化造成的。

所以如果遇到这种情况,最优的方法是断点,次优的方法是把对象序列化,强制执行一次快照,比如JSON.stringify

我们也可以理解为在这个例子中console.log打印的数据是引用类型,那么打印的只是一个快照{index: 1},等我们展开对象时会重新取值,此时值已经被改变,所以取出来的就是最新的值也就是{index: 2}了。

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改