一起console.log()引发的血案

4,045 阅读2分钟

案发现场:

let timing = window.performance.timing,
    timingStr = JSON.stringify(timing),
    domContentLoadedEventStart = timing.domContentLoadedEventStart
    console.log("timing:", timing)
    console.log("timingStr:", timingStr)
    console.log("domContentLoadedEventStart:", domContentLoadedEventStart)

以上代码执行结果:

三种方式打印出来的值竟然不一样!

推断取证:

  看上述结果:如果直接打印timing对象,可以拿到domContentLoadedEventStart的值,而经过JSON.stringify或者打印的是timing.domContentLoadedEventStart,值都不对。

  1. 难道是在这个三个console.log()执行的过程中,页面的performance.timing的值发生了改变,然后我将三个console.log()的执行顺序做了 调整,发现结果还是一样:只有直接打印timing拿到了值。
  2. 使用debugger看实时数据: 神奇的是,debugger状态下三种打印方式的值是相同的,timing.domContentLoadedEventStart都为0。
  3. 使用setTimeOut延时再使用三种打印方式,三个的值又全都有值,且值相同。

综合上述,不禁让我怀疑:console.log是不是异步执行?

真相:

  果不其然,domContentLoadedEventStart前后值的不同果然和console.log()有关系。

  原来, 控制台中console.log()是在打印对象的时候采用了懒加载的思路,在用户点击展开对象属性的时候,才会去获取该属性。所以就解释了为什么我们在控制台中看到的timing对象的domContentLoadedEventStart属性有值,而以字符串方式,或者直接打印该对象的value时为0。另外,在Chrome的控制台中,如果打印的对象,被改变过对象值,会在对象后面出现一个可点击的i号,点击这个i号,我们可以清楚的看见这个值,被改变了。

  这下终于真相大白了。 不过console.log()也是我们debugger的一种非常有用的手段,但是如果打印出来的不是正确的值,反而会误导我们。最后,我去MDN上console.log()的API,原来MDN早就有打印对象时需要做特殊处理的说明。

  😔,还是我太年轻。