结论
针对在浏览器控制台出现打印结果和代码执行顺序不一致这种「异步现象」。网上主要有两种说法,笔者这里更喜欢第一种,好理解且符合笔者的测试
- 浏览器出于优化的目地,默认不会展开所有对象,只有当手动点击展开时才去”读取对应的值“来进行展示。「点击」是一个代码执行完成之后的行为,所以对深层对象的打印总是最后时刻的快照。
- 不同的浏览器可能有自己的console.log实现机制,受限于I/O性能可能打印的时机会与代码执行的时机不匹配。也就是说如果打印的时机如果较为靠后,那么打印时因为引用型的数据发生了修改,所以出现改变前后打印结果都一致的现象
正文
console.log
是日常开发中用的较多的调试技巧。我们都知道他能把信息打印到控制台中。考虑下面的例子
const a=[{name:0}];
console.log(a);
a[0].name++;
console.log(a);
在我们的认为中应该先后打印
// [{name:0}]
// [{name:1}]
但是经笔者实际实验下来 「谷歌浏览器版本 93.0.4577.82(正式版本) (x86_64)」 。实际打印的和感觉上的差别很大,实际打印了
// [{name:1}]
// [{name:1}]
从这个结果看,好似console.log是一个异步的方法,打印是在代码执行完之后才进行的操作。 从「你不知道的JavaScript中卷」翻阅到
并没有什么规范或一组需求指定console.* 方法族如何工作——它们并不是JavaScript 正式 的一部分,而是由宿主环境(请参考本书的“类型和语法”部分)添加到JavaScript 中的。因此,不同的浏览器和JavaScript 环境可以按照自己的意愿来实现,有时候这会引起混淆。 尤其要提出的是,在某些条件下,某些浏览器的console.log(..) 并不会把传入的内容立即输出。出现这种情况的主要原因是,在许多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(从页面/UI 的角度来说)浏览器在后台异步处理控制台I/O 能够提高性能,这时用户甚至可能根本意识不到其发生。
好吧,上面一大段看的头疼。但大概意思就是说不同的浏览器可能有自己的console.log实现机制,受限于I/O性能可能打印的时机会与代码执行的时机不匹配。也就是说如果打印的时机如果较为靠后,那么打印时因为引用型的数据发生了修改,所以出现改变前后打印结果都一致的现象。这么一说貌似也能说的通。我们在看看下一个例子:
const c={age:0}
console.log(c);
c.age++
console.log(c);
//先后的打印结果为
//{age: 0}
//{age: 1}
看到这属实蚌埠住了,为啥这个例子又能符合我们的逻辑认知呢?运行示例代码看下图
const a=[{name:0}]
console.log(a,'初始:0 pre-a需展开');
a[0].name++
console.log(a,'初始:0 after-a需展开');
const b={info:{age:0}}
console.log(b,'初始:0 pre-b需展开');
b.info.age++
console.log(b,'初始:0 after-b需展开');
const c={age:0}
console.log(c,'初始:0 pre-c不需展开');
c.age++
console.log(c,'初始:0 after-c不需展开');
有什么共通之处?从图中可以看出浏览器控制对于对象和数组只能默认展开第一层,更深的对象是用“..."表示的。因此这里就有另一种说法,浏览器出于优化的目地,默认不会展开所有对象,只有当手动点击展开时才去”读取对应的值“来进行展示。这么一来上面的所有的示例在这个角度也能说的通。
参考资料
- 你不知道的JavaScript中卷
- juejin.cn/post/695610…