如何理解变量提升和 var、let、const间的区别

发布于:2025-03-20 ⋅ 阅读:(21) ⋅ 点赞:(0)

JavaScript 变量提升(Hoisting)是编译阶段的特殊机制,其核心原理如下:

一、变量提升底层机制

1️⃣ 编译阶段处理

  • 在代码执行前,JS 引擎会先解析代码并创建执行上下文
  • 对所有声明语句(varfunction)进行预处理

2️⃣ 存储空间划分

// 编译阶段会创建变量环境(VariableEnvironment):
{
  a: undefined,      // var 声明
  b: function(){...} // function 声明
}

二、提升规则

声明类型 提升行为 示例
var 变量 声明提升到作用域顶部,值初始化为undefined console.log(a); var a=1;undefined
function声明 整个函数体提升 可先调用再声明
let/const 存在暂时性死区(TDZ) 提前访问会报错

暂时性死区:暂时性死区(Temporal Dead Zone)是 ES6 中 let/const 声明的特性,指:从进入作用域到变量声明语句执行前的区域,此时访问变量会触发 ReferenceError。

三、执行阶段表现

// 原始代码
console.log(a);
var a = 1;

// 实际执行逻辑等同于:
var a; // 提升声明
console.log(a); // undefined
a = 1; // 原地赋值

四、变量提升特殊注意点

  1. 函数优先级:函数声明提升优先于变量声明
console.log(foo); // 输出函数体,而非undefined
var foo = 1;
function foo() {}
  1. 重复声明覆盖(如下代码案例):
function b() {
  console.log("第一次调用");
}
function b() {
  console.log("第二次调用");
} // 后者覆盖前者
  1. 块级作用域影响(ES6+):
{
  let a = 1;
  var b = 2;
}
console.log(b); // 2
console.log(a); // ReferenceError

五、变量提升最佳实践建议

  1. 使用 let/const 替代 var 避免意外提升
  2. 函数优先用表达式写法:const fn = () => {}
  3. 避免在同一作用域重复声明同名标识符

💡 现代开发中应通过 ESLint 规则 (如 no-use-before-define) 来规避变量提升带来的潜在问题。


六、varletconst 的核心区别对比

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
重复声明 ✅ 允许 ❌ 禁止 ❌ 禁止
变量提升 ✅ 提升且初始化为undefined ⚠️ 提升但存在 TDZ ⚠️ 提升但存在 TDZ
初始化要求 可后赋值 可后赋值 ❗ 必须立即赋值
全局对象属性 ✅ (如 window.a)
重新赋值 ✅ 允许 ✅ 允许 ❌ 禁止(对象属性可修改)

七、关键差异详解

1. 作用域范围
// var:函数作用域
function testVar() {
  if (true) {
    var a = 1;
  }
  console.log(a); // 1(泄露到函数作用域)
}

// let/const:块级作用域
function testLet() {
  if (true) {
    let b = 2;
    const c = 3;
  }
  console.log(b); // ReferenceError
  console.log(c); // ReferenceError
}
2. 变量提升与暂时性死区(TDZ)
// var 的变量提升
console.log(x); // undefined
var x = 5;

// let/const 的 TDZ
console.log(y); // ❌ ReferenceError
let y = 10;
3. 重复声明
var name = "Alice";
var name = "Bob"; // ✅ 允许

let age = 25;
let age = 30; // ❌ SyntaxError

const PI = 3.14;
const PI = 3.14159; // ❌ SyntaxError
4. const 的特殊性
const user = { name: "Tom" };
user.name = "Jerry"; // ✅ 允许(修改对象属性)
user = {}; // ❌ TypeError(禁止重新赋值)

const arr = [1, 2];
arr.push(3); // ✅ 允许(修改数组内容)
arr = [4, 5]; // ❌ TypeError

八、最佳实践建议

  1. 优先使用 const
    声明后不需修改的变量(如配置项、数学常量)

  2. 需要重新赋值时用 let
    循环计数器、状态变量等

  3. 避免使用 var
    防止变量泄露和意外覆盖(ES6+项目)

  4. 注意循环陷阱

    // var 的陷阱
    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i)); // 输出 3, 3, 3
    }
    
    // let 的正确用法
    for (let j = 0; j < 3; j++) {
      setTimeout(() => console.log(j)); // 输出 0, 1, 2
    }
    

九、常见问题

  1. TDZ 的表现
    let/const 声明前访问会触发错误,而 varundefined

  2. 全局污染问题

    var globalVar = 1;
    console.log(window.globalVar); // 1
    
    let moduleVar = 2;
    console.log(window.moduleVar); // undefined
    
  3. 函数提升优先级
    函数声明优先于 var 变量提升:

    console.log(typeof func); // "function"
    var func = "变量";
    function func() {}