JavaScript 变量声明详解:var、let、const 的核心差异

在 JavaScript 中,varletconst 三种变量声明方式的行为差异是新手常踩的 "坑",尤其是报错场景往往具有迷惑性。本文将从三者的特性出发,结合具体案例解析核心差异,并专门整理常见报错类型及原因,帮助你彻底理清它们的用法。

⭐一、var:历史遗留的变量声明方式

var 是 JavaScript 早期唯一的变量声明方式,因设计缺陷在现代开发中已不推荐使用,其核心特性如下:

🌼1. 函数级作用域,无块级作用域

var 声明的变量仅受函数边界限制,不受 {}iffor 等块级作用域约束,容易导致变量 "溢出"。

javascript 复制代码
// 4.js 案例
{
  var age = 18; // 块级作用域无法限制 var
  let height = 188; // let 受块级作用域限制
}
console.log(age); // 18(正常输出,var 溢出到外部)
console.log(height); // 报错:height is not defined(块外不可访问)

🌼2. 变量提升:声明提前,赋值滞后

var 会将声明提升到作用域顶部,但赋值仍在原位置执行,导致变量在声明前可被访问(值为 undefined)。

javascript 复制代码
// 2.js 案例
console.log(age); // undefined(声明被提升,赋值未执行)
var age = 18; 
// 等价于:
// var age; (提升到顶部)
// console.log(age);
// age = 18;

🌼3. 允许重复声明和重新赋值

同一作用域内可重复声明同一变量,后声明的会覆盖前声明的,易引发意外修改。

javascript 复制代码
var name = "张三";
var name = "李四"; // 无报错,覆盖前值
console.log(name); // 李四

⭐二、let:ES6 新增的块级作用域变量

let 是 ES6 为解决 var 缺陷引入的声明方式,专为 "可修改的变量" 设计,核心特性如下:

🌼1. 块级作用域:变量仅在当前代码块有效

let 声明的变量被限制在最近的 {} 内,包括循环、条件判断等语句块,有效避免变量污染。

javascript 复制代码
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // 输出 0、1、2(每次迭代的 i 独立)
}
console.log(i); // 报错:i is not defined(循环外不可访问)

🌼2. 暂时性死区(TDZ):禁止提前访问

let 不存在变量提升,在声明语句执行前,变量处于 "暂时性死区",无法被访问,否则报错。

javascript 复制代码
// 2.js 案例
console.log(height); // 报错:Cannot access 'height' before initialization
let height = 188; // 声明后才可用

🌼3. 不允许重复声明,但允许重新赋值

同一作用域内不可重复声明,但可修改变量值。

javascript 复制代码
let age = 18;
// let age = 20; // 报错:Identifier 'age' has already been declared
age = 20; // 允许重新赋值
console.log(age); // 20

⭐三、const:声明不可重新赋值的常量

constlet 特性基本一致,核心区别是声明后不可重新赋值,专为 "常量" 设计:

🌼1. 声明时必须初始化,且不可重新赋值

const 声明必须同时赋值,赋值后不能修改引用(简单类型值不可变,复杂类型引用不可变)。

javascript 复制代码
// 1.js 案例
const key = "abc123";
key = "abc234"; // 报错:Assignment to constant variable

🌼2. 复杂类型的 "常量":属性可修改,引用不可变

对象 / 数组的内部属性可修改,但不能重新赋值为新的对象 / 数组。

javascript 复制代码
// 3.js 案例
const person = { name: "ysw", age: 28 };
person.age = 21; // 允许修改属性
console.log(person); // { name: "ysw", age: 21 }

person = { name: "new" }; // 报错:Assignment to constant variable

🌼3. 与 let 相同的作用域和暂时性死区

const 同样支持块级作用域,且存在暂时性死区,行为与 let 一致。

⭐四、常见报错集合及原因解析

在使用 varletconst 时,以下报错最为常见,需重点掌握:

  1. ReferenceError: height is not defined

    • 原因:访问了未声明的变量,或变量在作用域之外被调用。
    • 案例:let/const 声明的变量在块级作用域外被访问(如 4.js 中 console.log(height))。
  2. TypeError: Assignment to constant variable

    • 原因:对 const 声明的变量重新赋值。
    • 案例:1.js 中 key = 'abc234',试图修改 const 变量的值。
  3. ReferenceError: Cannot access 'PI' before initialization

    • 原因:在 let/const 声明前访问变量,触发暂时性死区(TDZ)。
    • 案例:2.js 中 console.log(PI) 写在 const PI = ... 之前。
  4. SyntaxError: Identifier 'age' has already been declared

    • 原因:在同一作用域内用 let/const 重复声明同一变量(包括与 var 重复)。
    • 案例:let age = 18; let age = 20;

⭐五、var、let、const 总结差异对比表

对比维度 var let const
作用域范围 函数级(块级无法约束) 块级({} 内严格约束) 块级({} 内严格约束)
暂时性死区(TDZ ❌ 无,声明前访问返回 undefined ✅ 有,声明前访问抛出 ReferenceError ✅ 有,声明前访问抛出 ReferenceError
变量提升与 TDZ 关系 声明完全提升(无 TDZ 干扰) 声明提升但被 TDZ 拦截访问 声明提升但被 TDZ 拦截访问
重复声明允许性 ✅ 允许同作用域重复声明 ❌ 禁止,重复声明直接报错 ❌ 禁止,重复声明直接报错
重新赋值可行性 ✅ 无限制,可任意重新赋值 ✅ 允许重新赋值 ❌ 禁止,赋值后不可改引用
声明时初始化要求 ❌ 可选(var x; 合法) ❌ 可选(let x; 合法) ✅ 必须(const x; 直接报错)
全局声明与 window 关系 会成为 window 属性(污染全局) 独立存在(不污染 window) 独立存在(不污染 window)
TDZ 典型触发案例 无(无 TDZ 机制) console.log(a); let a = 1; 报错 console.log(b); const b = 2; 报错

⭐六、最佳实践总结

  1. 优先使用 const :对于不需要修改的变量(尤其是对象、函数),用 const 明确语义,减少意外修改。
  2. 按需使用 let :仅当变量需要重新赋值(如循环计数器、状态变量)时使用 let
  3. 彻底抛弃 var:避免其函数级作用域和变量提升带来的隐蔽 bug。
  4. 处理复杂对象 :用 Object.freeze() 冻结不需要修改的对象,增强代码安全性。
相关推荐
T___T3 小时前
Git 入门实战笔记:从 0 到 1 掌握代码版本管理流程
git·面试
豆苗学前端3 小时前
10分钟带你入门websocket,并实现一个在线多人聊天室
前端·javascript·后端
白水清风3 小时前
Vue3之渲染器
前端·vue.js·面试
白水清风3 小时前
Vue3之组件化
前端·vue.js·面试
luckyPian3 小时前
ES6+新特性:ES7(二)
开发语言·javascript·ecmascript
边洛洛3 小时前
解决[PM2][ERROR] Script not found: D:\projects\xxx\start
前端·javascript
白水清风3 小时前
Vue3之响应式系统
vue.js·面试·前端工程化
农夫山泉的小黑4 小时前
【DeepSeek帮我准备前端面试100问】(十八)Reflect在vue3的使用
前端·面试
Achieve前端实验室4 小时前
【每日一面】手写防抖函数
前端·面试·node.js