关于JavaScript提升、暂存死区的代码运行差异问题及资料推荐
变量提升(Hoisting)与暂时性死区(TDZ)的差异解析
哈哈,这个问题问到点子上了——这两个代码的差异确实和变量提升(hoisting)密切相关,而且刚好和暂时性死区(TDZ)的规则交织在一起,咱们一点点拆解来看:
先看能正常运行的按钮点击代码
对应的JavaScript代码:
const btn = document.getElementById("btn"); btn.onclick = function() { printMe(obj); }; function printMe(person) { console.log(person.firstName, person.lastName); } const obj = { firstName: "John", lastName: "Wick" };
配套的HTML:
<button id="btn">Click me!</button>
这个代码能跑的核心原因是按钮点击是异步触发的:当页面加载完成后,obj已经被成功初始化,脱离了暂时性死区;同时printMe作为函数声明,会被完全提升到当前作用域的顶部——所以当你点击按钮触发事件时,printMe已经可用,obj也早就准备好,自然能正常打印结果。
再看无法运行的直接调用代码
printMe(obj); function printMe(person) { console.log(person.firstName, person.lastName); } const obj = { firstName: "John", lastName: "Wick" };
这里的问题出在执行顺序上:
- 函数声明
printMe会被完全提升(包括函数体),所以第一行调用printMe的时候,函数本身是存在的; - 但
obj是用const声明的,它的声明会被提升,但不会被初始化——这就进入了暂时性死区(TDZ)。在执行到const obj = ...这行代码之前,任何对obj的引用都会抛出ReferenceError。所以当第一行调用printMe(obj)时,obj还在TDZ里,直接报错。
核心结论
这两个代码的差异本质就是不同类型声明的提升规则差异:
- 函数声明会被完全提升,在作用域内任何位置都能调用;
let/const声明的变量只会提升声明,不会提升初始化,在声明语句执行前处于TDZ,无法被引用。
推荐阅读资料
- MDN文档中的「变量提升(Hoisting)」章节,详细讲解了不同类型声明的提升规则
- MDN文档中的「暂时性死区(Temporal Dead Zone)」章节,深入解释TDZ的触发场景和影响
- 《你不知道的JavaScript(上卷)》的「作用域与闭包」部分,对提升和TDZ有非常透彻的实战解析
内容的提问来源于stack exchange,提问作者d_oram
相关产品推荐
相关产品推荐

