
本文深入解析 var 在函数内部声明时因变量提升(hoisting)和作用域遮蔽(shadowing)导致读取为 undefined 的根本原因,并对比 let 的不同行为,帮助开发者准确理解 JavaScript 执行机制。
本文深入解析 `var` 在函数内部声明时因变量提升(hoisting)和作用域遮蔽(shadowing)导致读取为 `undefined` 的根本原因,并对比 `let` 的不同行为,帮助开发者准确理解 javascript 执行机制。
在 JavaScript 中,var 声明具有函数作用域和变量提升特性——这意味着所有 var 声明(无论出现在函数体何处)都会被“提升”到函数顶部,但只有声明被提升,赋值不会。当函数内声明了一个与外层同名的 var 变量时,就会发生变量遮蔽(variable shadowing):内部变量覆盖外部变量的访问路径,导致在赋值前读取该标识符时返回 undefined。
以下代码清晰体现了这一机制:
var numOfBeers = 5;
function getMoreBeers() {
console.log('I have this many beers: ' + numOfBeers); // 输出: "I have this many beers: undefined"
var numOfBeers = 25; // 声明被提升,赋值留在原位
return numOfBeers;
}
console.log('I now have this many beers: ' + getMoreBeers());
// 输出:
// I have this many beers: undefined
// I now have this many beers: 25等价于(经引擎自动提升后):
var numOfBeers = 5;
function getMoreBeers() {
var numOfBeers; // 提升后的声明:numOfBeers 初始化为 undefined
console.log('I have this many beers: ' + numOfBeers); // 此时访问的是局部变量,值为 undefined
numOfBeers = 25; // 赋值发生在声明之后
return numOfBeers;
}⚠️ 注意:此时 console.log 访问的并非外层的 numOfBeers = 5,而是已被遮蔽的、尚未赋值的局部变量,因此结果为 undefined。
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
相比之下,全局作用域中未包裹函数时,var 提升仍会发生,但由于没有嵌套作用域干扰,效果表现为“可提前访问已声明未赋值的变量”:
console.log('I have this many beers: ' + numOfBeers); // 输出: "I have this many beers: 25"
var numOfBeers = 25; // 声明提升,赋值保留;实际执行顺序等价于:var numOfBeers; console.log(...); numOfBeers = 25;而使用 let 或 const 则完全不同:它们具有块级作用域,且存在暂时性死区(TDZ)。在声明前访问会直接抛出 ReferenceError,而非返回 undefined:
console.log(numOfBeers); // ❌ Uncaught ReferenceError: Cannot access 'numOfBeers' before initialization let numOfBeers = 25;
更关键的是,let 不允许在同一作用域内重复声明同一标识符——这也是你遇到 Identifier 'numOfBeers' has already been declared 错误的原因,它从语法层面阻止了遮蔽式错误。
✅ 最佳实践建议:
- 优先使用 const / let 替代 var,避免意外遮蔽与提升陷阱;
- 若需修改外层变量,请显式省略内部 var/let/const 声明,或使用不同名称避免遮蔽;
- 如确需在函数内更新外层变量,可直接赋值(无需重新声明):
var numOfBeers = 5;
function getMoreBeers() {
console.log('I have this many beers: ' + numOfBeers); // ✅ 输出 5(访问外层变量)
numOfBeers = 25; // 直接修改外层变量
return numOfBeers;
}理解 var 的提升与遮蔽机制,不仅是掌握 JavaScript 执行模型的关键一步,更是写出可预测、易维护代码的坚实基础。

















