🌟 ​**​《彻底搞懂 JavaScript 的var、let、const》​** 🌟

129 阅读3分钟

一、变量提升的本质:JS 的「预编译魔术」

console.log(a); // undefined(而不是报错!)
var a = 10;
  • var 的规则
    声明会被隐形搬运到当前作用域顶部,但赋值保持原位,相当于:

    var a;          // 声明被提升
    console.log(a); // 此时 a=undefined
    a = 10;         // 赋值留在原地
    
  • let/const 的差异
    虽然也有提升,但会进入 ​TDZ(暂时性死区)​,在声明前访问直接报错:

    console.log(b); // ❌ ReferenceError
    let b = 20; 
    

二、作用域战场:var vs let/const

varlet/const
作用域类型函数级(整个函数内有效)块级({} 内有效)
重复声明✅ 允许❌ 禁止
全局污染自动成为 window 属性与 window 对象隔离

经典陷阱对比

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

三、const 的深层原理:锁柜门还是锁物品?

const ID = 100;
ID = 200; // ❌ 报错(基本类型不可变)

const user = {name: 'Alice'};
user.name = 'Bob'; // ✅ 允许(对象属性可变)
user = {};         // ❌ 报错(地址不可变)
  • 底层逻辑

    • 简单类型(Number/String等)​:直接存储在栈内存const 冻结值
    • 复杂类型(Object/Array等)​:数据在堆内存const 只冻结栈中存储的内存地址​(类似锁住储物柜门,不锁柜内物品)

四、堆栈视角:值传递 vs 地址传递

简单数据类型(值传递)复杂数据类型(地址传递)
存储位置栈内存(直接存值)堆内存存数据,栈内存存地址指针
赋值行为复制整个值(类似复印文件)复制地址指针(类似给储物柜钥匙)
典型案例let a=1; b=a;(b 独立)let arr1=[]; arr2=arr1;(共享数据)

代码示例

// 值传递(独立副本)
let x = 10;
let y = x; 
y = 20;
console.log(x); // 10(不受影响)

// 地址传递(共享数据)
let obj1 = {count: 10};
let obj2 = obj1;
obj2.count = 20;
console.log(obj1.count); // 20(同步修改)

五、三大声明终极对比表

特性varletconst
作用域函数级块级块级
提升表现声明提升,值=undefined声明提升,但触发 TDZ同 let
重复声明
值修改权简单类型❌ / 对象属性✅
内存管理可能内存泄漏块结束自动释放同 let

🔥 核心总结

  1. 变量提升是 JS 引擎的「预扫描」,var 声明会被提到作用域顶部但未赋值,let/const 会进入 TDZ 禁区
  2. 简单数据类型在栈内存独立存储,赋值时复制值复杂数据类型在堆内存共享存储,赋值时复制地址
  3. 默认使用 const,需要修改变量时用 let,除非维护老代码否则不用 var

一句话记忆

  • var 是随意张贴的小广告(随处存在,可覆盖)
  • let 是精准坐标的公告板(块级可控,可修改)
  • const 是带玻璃罩的展示柜(地址锁定,内容可调)

(用 console 多实践这些特性,理解会更深刻哦!💻)