使用DOM API时JavaScript为何不遵循常规顺序执行?
为什么DOM对象的console.log输出不符合JS顺序执行预期?
普通对象的正常执行示例
function tellMeWhatsPassed(thing){ console.log(thing) } function createThing(){ const div = {} div.innerHTML = 'original' tellMeWhatsPassed(div) div.innerHTML = 'updated' tellMeWhatsPassed(div) } createThing() // 输出: // {innerHTML: 'original'} // {innerHTML: 'updated'}
执行顺序说明
JS会先声明tellMeWhatsPassed和createThing函数,调用createThing()时进入其执行上下文:创建空对象并赋值innerHTML: 'original',调用函数输出该对象;接着修改innerHTML为updated,再次调用函数输出更新后的对象,完全符合逐行顺序执行逻辑。
DOM元素的特殊输出示例
function tellMeWhatsPassed(thing){ console.log(thing) } function createThing(){ const div = document.createElement('div') div.innerHTML = 'original' tellMeWhatsPassed(div) div.innerHTML = 'updated' tellMeWhatsPassed(div) } createThing() // 输出(控制台展开后): // <div> updated </div> // <div> updated </div>
原因解析
JS的顺序执行根本没被打破,代码确实是按顺序先打印DOM元素、再修改innerHTML、再打印的。问题出在浏览器控制台对DOM元素的打印机制:
- 当你用
console.log打印DOM元素时,控制台并不会立即生成该元素状态的快照,而是只保存一个指向这个DOM元素的引用。 - 当你在控制台中点击展开这个DOM元素查看详情时,浏览器才会去读取该元素当前的实时状态——而此时你的代码已经完成了
innerHTML的修改,所以两次打印的引用指向的都是已经更新后的DOM元素,看起来就像两次输出都是修改后的值。
而普通对象不存在这个问题,因为console.log打印普通对象时,会默认快照它当前的可枚举属性状态,后续修改不会影响已经打印的快照内容。
内容的提问来源于stack exchange,提问作者OReilly
相关产品推荐
相关产品推荐

