JavaScript 声明提升

张开发
2026/4/17 7:23:22 15 分钟阅读

分享文章

JavaScript 声明提升
JavaScript 声明提升 (Hoisting) 学习笔记声明提升是 JavaScript 中一个非常独特且容易让人困惑的机制。它指的是在代码执行之前JavaScript 引擎会将变量和函数的声明部分“提升”到当前作用域的顶部但赋值部分不会提升。这意味着无论你在代码的哪个位置声明变量或函数它们都像是在作用域顶部被声明了一样。一、核心机制提升什么不提升什么理解提升的关键在于区分声明 (Declaration)和赋值 (Initialization/Assignment)。声明方式声明提升赋值提升提升后的行为var✅ 是❌ 否初始化为undefinedlet/const✅ 是 (语法层面)❌ 否进入暂时性死区 (TDZ)访问报错function(声明)✅ 是✅ 是 (整个函数体)函数完全可用function(表达式)✅ 是 (变量部分)❌ 否变量为undefined调用报错二、var的声明提升var是最典型的提升案例。代码执行前var声明会被提升到作用域顶部并初始化为undefined。1. 基本示例console.log(age);// 输出undefined (不报错)varage25;console.log(age);// 输出25引擎实际执行顺序逻辑上varage;// 声明提升初始化为 undefinedconsole.log(age);// undefinedage25;// 赋值console.log(age);// 252. 函数内的提升functiontest(){console.log(name);// undefinedvarnameAlice;console.log(name);// Alice}test();等价于functiontest(){varname;// 提升console.log(name);// undefinednameAlice;console.log(name);// Alice}3. 重复声明var允许在同一作用域内重复声明同一个变量不会报错后面的声明会被忽略。vara1;vara2;console.log(a);// 2三、let和const的声明提升与 TDZlet和const也会被提升但它们的行为与var截然不同。1. 暂时性死区 (Temporal Dead Zone, TDZ)在代码执行流到达声明语句之前变量处于“未初始化”状态。此时访问该变量会抛出ReferenceError。这个区域称为TDZ。console.log(age);// ❌ ReferenceError: Cannot access age before initializationletage25;为什么虽然声明被提升了但初始化赋值没有。在let age 25;这一行执行之前age存在于作用域中但不可访问。2.const必须初始化const声明必须在声明时立即赋值否则报错。constPI;// ❌ SyntaxError: Missing initializer in const declarationconstPI3.14;// ✅3. 块级作用域的影响let和const是块级作用域Block Scope提升仅限于当前块{}内。if(true){console.log(x);// ❌ ReferenceErrorletx10;}四、函数声明 vs 函数表达式这是提升中最容易混淆的地方。1. 函数声明 (Function Declaration)完全提升。函数名和函数体都会被提升到作用域顶部。可以在声明之前调用。sayHello();// ✅ 输出Hello!functionsayHello(){console.log(Hello!);}等价于functionsayHello(){console.log(Hello!);}sayHello();2. 函数表达式 (Function Expression)只提升变量声明不提升赋值。变量初始化为undefined。如果在赋值前调用会报错TypeError。sayHi();// ❌ TypeError: sayHi is not a functionvarsayHifunction(){console.log(Hi!);};等价于varsayHi;// 声明提升值为 undefinedsayHi();// ❌ TypeError: sayHi is not a functionsayHifunction(){...};// 赋值3. 箭头函数箭头函数本质上是函数表达式行为与var 函数表达式一致。constadd(a,b)ab;// 如果用 varvaradd(a,b)ab;// 在赋值前调用 add 会报错五、混合提升场景当var、let、const、函数声明混用时提升优先级和规则如下函数声明优先级最高完全提升。var声明次之提升并初始化为undefined。let/const最后提升但进入 TDZ。示例 1同名变量冲突console.log(a);// undefined (var 提升)vara1;leta2;// ❌ SyntaxError: Identifier a has already been declared注意在同一作用域内不能同时用var和let/const声明同名变量。示例 2函数与变量同名console.log(test);// 输出函数定义 (函数声明优先)vartest100;console.log(test);// 输出 100 (var 赋值覆盖了函数)functiontest(){returnfunction;}执行流程函数test被完全提升。var test声明被提升但被函数声明覆盖不重复声明。执行console.log(test)- 输出函数。执行var test 100- 变量test被赋值为 100。执行console.log(test)- 输出 100。六、常见陷阱与最佳实践1. 陷阱在声明前使用var// 容易写出难以调试的 bugif(condition){vardatafetchData();}console.log(data);// 即使 condition 为 falsedata 也是 undefined而不是报错解决使用let或const利用 TDZ 在错误使用时立即报错。2. 陷阱循环中的varvarfuncs[];for(vari0;i3;i){funcs.push(function(){console.log(i);});}funcs[0]();// 输出 3 (所有函数共享同一个 i)funcs[1]();// 输出 3funcs[2]();// 输出 3解决使用letlet是块级作用域每次循环都会创建新的i。letfuncs[];for(leti0;i3;i){funcs.push(function(){console.log(i);});}funcs[0]();// 输出 0funcs[1]();// 输出 1funcs[2]();// 输出 23. 最佳实践始终使用let和const避免var带来的提升陷阱和变量污染。声明在前使用在后养成在作用域顶部声明变量的习惯虽然现代 JS 不强制但可读性好。不要依赖提升即使函数声明可以提前调用也建议将函数定义放在调用之前提高代码可读性。利用 TDZ如果代码在声明前访问了变量let/const会立即报错帮助你快速发现逻辑错误。七、总结特性varlet/const函数声明声明提升✅ 是✅ 是 (语法上)✅ 是初始化提升✅ 是 (undefined)❌ 否 (TDZ)✅ 是 (整个函数)作用域函数作用域块级作用域函数作用域重复声明✅ 允许❌ 不允许❌ 不允许访问未初始化变量返回undefined抛出ReferenceErrorN/A一句话总结JavaScript 引擎在执行代码前会先扫描并提升声明。var提升并初始化为undefinedlet/const提升但进入“暂时性死区”函数声明则完全提升。为了代码的健壮性和可读性请优先使用let和const并遵循“先声明后使用”的原则。

更多文章