写 ref="/tag/137/" style="color:#3D6345;font-weight:bold;">JavaScript 的时候,你有没有遇到过变量还没定义就能访问的情况?比如下面这段代码:
console.log(name);
var name = '小明';
很多人第一反应会觉得这应该报错,但实际输出是 undefined,而不是报错。这就是“变量提升”在作怪。
什么是变量提升
JavaScript 在执行代码前会先做一轮“预处理”,把变量和函数的声明提到当前作用域的最前面。注意,只是“声明”被提升了,赋值还留在原地。
var name;
console.log(name); // undefined
name = '小明';
上面这段才是 JavaScript 实际执行的样子。虽然代码没报错,但这种行为很容易让人误解变量已经存在且有值。
let 和 const 也提升吗
很多人以为 let 和 const 没有提升,其实它们也有提升,只不过进入了“暂时性死区”。
console.log(age);
let age = 18;
这段代码会直接报错:ReferenceError: Cannot access 'age' before initialization。看起来像是没有提升,其实是提升了但不让访问,直到赋值那一行。
函数声明和变量声明的冲突
当函数和变量同名时,提升顺序也会带来意外结果。
console.log(typeof foo);
function foo() {}
var foo = 'bar';
你猜输出什么?是 function。因为在提升阶段,函数声明优先于变量声明。相当于:
function foo() {}
console.log(typeof foo); // function
foo = 'bar';
实际开发中的陷阱
项目里有个同事写了个判断登录状态的函数:
if (!isLoggedIn) {
console.log('请先登录');
}
var isLoggedIn = true;
他本意是想检查是否已登录,结果提示“请先登录”。原因就是 isLoggedIn 被提升了,值为 undefined,取反后成了 true,条件成立。
这种问题在调试时很难一眼看出来,尤其在复杂的逻辑块里,变量散落在各处,读代码的人得在脑子里模拟一遍提升过程,特别费劲。
怎么避免这些问题
最简单的办法:把变量声明放在作用域顶部。现代开发中基本都用 let 和 const,它们不会给你“能访问未初始化变量”的错觉。
let username = '张三';
let isLogin = true;
if (isLogin) {
console.log(`${username} 已登录`);
}
另外,保持一个习惯:声明即赋值,尽量别拆开。这样不管有没有提升,逻辑都是清晰的。
团队协作时更要注意,每个人的编码习惯不同,一个不小心就可能写出看似正常实则危险的代码。用 ESLint 这类工具也能提前发现潜在问题。