javascript中同名变量声明会屏蔽外层变量,导致内部引用始终指向内部声明;var会提升并初始化为undefined,let/const存在tdz且禁止重复声明,函数声明也参与屏蔽且优先级更高。

JavaScript中,当函数内部声明了与外部作用域同名的变量时,内部变量会屏蔽(shadow)外部变量,导致函数内对这个标识符的引用始终指向内部声明,而不会向上查找外部同名变量。
作用域链与屏蔽发生的位置
JavaScript采用词法作用域,函数执行时会构建一条作用域链:从当前函数的局部作用域开始,逐级向上查找(函数作用域 → 外层函数作用域 → 全局作用域)。但一旦在当前作用域中找到该变量的声明(无论是否已赋值),查找就立即停止,不再继续向上。
- 变量声明(var、let、const)都会触发屏蔽
-
var 存在变量提升,即使声明在后,也会被视作在函数顶部存在(但初始化为
undefined) - let/const 存在暂时性死区(TDZ),在声明前访问会报错,但依然屏蔽外层同名变量
不同声明方式下的屏蔽表现
以下代码演示三种常见情况:
(1)var 屏蔽:
let x = 10;
function foo() {
console.log(x); // undefined(不是10!因为 var x 提升了)
var x = 20;
}
foo();
(2)let 屏蔽 + TDZ:
let x = 10;
function bar() {
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 20;
}
bar();
(3)参数与变量同名:
let y = 100;
function baz(y) {
console.log(y); // 输出传入的实参值,不是外层的100;形参 y 屏蔽了外层 y
let y = 30; // SyntaxError:同一作用域中不能重复声明 y(let/const 不允许重复声明)
}
如何避免意外屏蔽
屏蔽本身是语言设计的正常行为,但容易引发误解。建议:
- 避免在嵌套作用域中刻意复用外层变量名,尤其在函数内部定义新变量时
- 使用更具描述性的变量名,降低命名冲突概率(如
outerCount和innerCount) - 借助 ESLint 规则(如
no-shadow)在开发阶段提示潜在屏蔽问题 - 理解
var提升和let/const的 TDZ 行为,不依赖“未声明即查外层”的直觉
注意:函数声明也参与屏蔽
函数声明同样遵循作用域链规则,并可屏蔽外层同名变量或函数:
const fn = () => console.log('global');
function test() {
fn(); // TypeError: fn is not a function(因为下面的函数声明 fn 屏蔽了外层 fn)
function fn() { console.log('local'); }
}
test();
这是因为函数声明会被提升到当前作用域顶部,且优先级高于变量声明,因此它在执行前就已存在于当前作用域中,直接屏蔽外层同名绑定。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











