console.log打印对象的属性,外层与展开的属性值不一致问题

1.先看问题:

我们会发现 console.log() 打印出来的东西外层和展开的 like 对应的值不一样,出现了异步输出的情况

仔细观察,异常输出的时候,没展开的时候,显示的 like 值是‘唱跳rap’,点击箭头展开对象里的 like 则是‘打篮球’,而且,此时的输出值 ‘打篮球’ 执行语句,明显是在赋值语句obj.per.like = '打篮球'之前调用的,展开的时候,却变成了‘打篮球’

2.分析原因:

有一种说法是:console.log() 是一个异步的方法,浏览器在处理的时候对于这种读取 i/o 的操作一般会放到比较靠后的位置去处理。所以有些时候打印出来的值不符合预期。

还有一种说法是,当在打印一个对象的时候,由于对象中的值是引用的内存中的值,所以就会存在当打印对象的时候,当引用到对象中的值的时候,对象中的值已经发生变化了。所以才会存在打印结果与预期不一致的情况。

首先,JS中对象是引用类型,每次使用对象时,都只是使用了对象在堆中的引用。

当我们在使用 obj.per.like = '打篮球'改变了对象的属性值时,它在堆中 like 的值也变成了 '打篮球',而当我们不展开对象看的时候,console.log打印的是对象当时的快照,所以我们看到的 like属性值是没改变之前的'唱跳rap',展开对象时,它其实是重新去内存中读取对象的属性值,所以当我们展开对象后看到的like属性值是打篮球

3.浏览器或者可以说是开发者工具为什么会有这样的表现?

在《你不知道的javascript中卷》第二部分异步和性能1.1节异步控制台部分有提及:

There is no specification or set of requirements around how the console.* methods work -- they are not officially part of JavaScript, but are instead added to JS by the hosting environment (see the Types & Grammar title of this book series).
So, different browsers and JS environments do as they please, which can sometimes lead to confusing behavior.
In particular, there are some browsers and some conditions that console.log(..) does not actually immediately output what it's given. The main reason this may happen is because I/O is a very slow and blocking part of many programs (not just JS). So, it may perform better (from the page/UI perspective) for a browser to handle console I/O asynchronously in the background, without you perhaps even knowing that occurred.

翻译:

并没有什么规范或一组需求指定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 的异步化造成的。

书中建议:

如果遇到这种少见的情况,最好的选择是在JavaScript 调试器中使用断点,而不要依赖控制台输出。次优的方案是把对象序列化到一个字符串中,以强制执行一次“快照”,比如通过JSON.stringify(..)。

4.结论:

由此可见,console.log打印出来的内容并不是一定百分百可信的内容。一般对于基本类型 number、string、boolean、null、undefined 的输出是可信的。但对于 Object 等引用类型来说,则就会出现上述异常打印输出。

所以对于一般基本类型的调试,调试时使用console.log来输出内容时,不会存在坑。但调试对象时,最好还是使用打断点(debugger)这样的方式来调试更好。

5.注意:

还有一种情况就是这条数据已经被服务器保存了,所以会出现打印一个代码中不存在的值。

这种情况下建议换一条新数据进行测试。 

 

 

  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值