JavaScript变量提升指声明被编译阶段提前处理至作用域顶部,但仅声明被提升、赋值保留原位;var声明提升后值为undefined,函数声明整体提升,函数表达式仅变量名提升,let/const因TDZ在声明前访问抛出ReferenceError。

JavaScript变量提升(Hoisting)是指变量和函数声明在代码执行前被“移动”到其作用域顶部的现象。它不是真正的物理移动,而是JavaScript引擎在编译阶段将声明部分提前处理的结果。这种机制只影响声明,不包括初始化或赋值。
变量提升只提升声明,不提升赋值
使用var声明的变量,会被提升到作用域顶部,但值仍为undefined;而赋值操作保留在原位置。
console.log(a); // undefined(不是ReferenceError) var a = 10; console.log(a); // 10
上面的代码实际等价于:
var a; // 声明被提升 console.log(a); // 此时a尚未赋值,输出undefined a = 10; // 赋值保留在原处 console.log(a);
函数声明也会被提升,且整个函数体都被提升
函数声明(function foo() {...})不仅名称被提升,函数定义也一同被提升,因此可以在声明前调用。
立即学习“Java免费学习笔记(深入)”;
foo(); // 正常输出"hello"
function foo() {
console.log("hello");
}
但函数表达式(var foo = function() {...})只提升变量名,不提升函数体,调用会报错:
bar(); // TypeError: bar is not a function
var bar = function() {
console.log("world");
};
let和const不存在变量提升?其实是“暂时性死区”(TDZ)
let和const声明不会被提升到作用域顶部,但在声明之前访问它们会抛出ReferenceError,而不是返回undefined。这是因为它们存在“暂时性死区”——从块级作用域开始到声明语句执行前的区域。
console.log(x); // ReferenceError: Cannot access 'x' before initialization let x = 5;
- TDZ的存在是为了避免在声明前误用变量
- typeof对未声明的
let变量也不安全(同样报错),而var下是"undefined"
为什么会有变量提升?根源在于JavaScript的编译-执行两阶段机制
JavaScript引擎(如V8)在执行代码前会先进行编译(更准确说是“解析+预处理”),此时完成作用域分析、函数声明收集、var变量声明注册等。这是语言设计的历史选择,早期为了支持函数间相互调用(如递归或前置调用),让函数声明可被提前识别。
- 提升是引擎内部行为,开发者无法控制或禁用
- ES6引入
let/const后,通过TDZ修正了var带来的易错性 - 现代最佳实践:默认使用
const,需要重赋值时用let,避免var











