JS 变量声明避坑指南:彻底搞懂 var/let/const 的 3 大核心区别与最佳实践

前言:为什么我们要告别 var?

**

早期 JS 仅用于简单的页面交互,设计上存在不少 "反直觉" 的坑 ------var 就是典型代表。直到 ES6(2015)推出 let 和 const,才真正解决了 var 的缺陷,让 JS 更适合企业级开发。今天我们就从底层原理到实际用法,彻底理清这三者的区别,以及那些容易踩坑的细节。

一、JS 变量声明的演进:从 ES5 到 ES6

1. ES5 的唯一选择:var

var 是 ES5 中唯一的变量声明方式,但它的特性常常让人困惑:

  • 弱类型:变量类型由值决定,可随意修改(比如先存数字再存字符串)
  • 无块级作用域:只认函数作用域和全局作用域,块({})内声明的变量会 "泄露" 到外部
  • 变量提升(Hoisting) :编译阶段就会声明变量,但值为 undefined,执行阶段才赋值

看个直观的例子(来自你的笔记):

ini 复制代码
// 反直觉场景:变量还没声明,却能访问到
console.log(age); // 输出 undefined(不是报错!)
var age; // 编译阶段已"偷偷"声明,值为 undefined
age = 18; // 执行阶段才赋值
console.log(age); // 18
// 常量的"伪约定":大写变量只是习惯,实际能修改
var PI = 3.1415926;
PI = 3.15; // 不报错!
console.log(PI); // 3.15

这里的核心问题是 变量提升破坏了 "先声明后使用" 的逻辑,降低了代码可读性,还容易引发变量污染(比如不同块内重名的 var 变量会互相覆盖)。

2. ES6 的改进:let 和 const

为解决 var 的缺陷,ES6 新增了 let(声明变量)和 const(声明常量),它们的核心特性是:

  • 支持块级作用域
  • 不存在 "变量提升" 的直觉问题(实际会提升,但进入 "暂时性死区")
  • const 声明的常量必须初始化,且不能修改引用(简单类型不可改,复杂类型可改属性)

二、核心区别:一张表 + 代码案例

特性 var let const
块级作用域 ❌ 不支持 ✅ 支持 ✅ 支持
变量提升 ✅ 声明提升(值为 undefined) ✅ 提升但进入 "暂时性死区" ✅ 提升但进入 "暂时性死区"
重复声明 ✅ 允许 ❌ 不允许 ❌ 不允许
初始化要求 ❌ 可选 ❌ 可选 ✅ 必须初始化
值可修改 ✅ 允许 ✅ 允许 ❌ 简单类型不可改;复杂类型不可改引用

1. 块级作用域:let/const 解决 "变量泄露"

var 无视 {} 块级作用域,而 let/const 会被块级作用域限制:

arduino 复制代码
{
  var age = 18;    // var 不支持块级作用域
  let height = 188;// let 支持块级作用域
}
console.log(age);   // 18(变量泄露到外部)
console.log(height);// ReferenceError: height is not defined(块外无法访问)

这是 var 最常见的坑之一 ------ 比如在 for 循环中用 var 声明计数器,循环结束后变量仍会污染全局。

2. 暂时性死区(TDZ):let/const 避免 "提前访问"

var 会提前声明导致 "未定义却能访问",而 let/const 虽然也会在编译阶段提升,但会进入 "暂时性死区":在声明前访问,直接报错,强制遵循 "先声明后使用"。

ini 复制代码
// var 的情况:提前访问不报错,输出 undefined
console.log(age); // undefined
var age = 18;
// let/const 的情况:提前访问直接报错
console.log(PI);  // ReferenceError: Cannot access 'PI' before initialization
const PI = 3.14;
console.log(height); // ReferenceError: Cannot access 'height' before initialization
let height = 188;

3. const 的 "常量" 误区:不是所有值都不能改!

const 声明的 "常量",核心限制是 "不能修改引用地址" ,而非 "值不能改":

  • 简单类型(数字、字符串、布尔):值存在栈中,引用即值,所以不能改
  • 复杂类型(对象、数组):值存在堆中,引用是堆地址,所以 属性 / 元素可改,但不能重新赋值引用

看案例(来自你的笔记):

ini 复制代码
const person = {
  name: "ysw",
  age: 28
};
// 允许:修改对象属性(引用地址没变)
person.age = 21;
console.log(person); // { name: "ysw", age: 21 }
// 不允许:重新赋值引用(地址变了)
person = "hahaha"; // TypeError: Assignment to constant variable.

如果想让对象 "完全不可改",可以用 Object.freeze() 冻结对象(浅冻结,深冻结需递归处理):

ini 复制代码
const frozenPerson = Object.freeze(person);
frozenPerson.age = 22; // 严格模式下报错,非严格模式下修改无效
console.log(frozenPerson.age); // 21(值未变)

三、常见报错及解决方案

实际开发中,关于 var/let/const 的报错主要有 3 类,对应解决办法很明确:

报错信息 原因 解决方案
ReferenceError: xxx is not defined 块级作用域外访问 let/const 变量 在块内使用,或把声明提到块外
TypeError: Assignment to constant variable 试图修改 const 变量的值 / 引用 改用 let 声明,或不修改 const 引用
ReferenceError: Cannot access 'xxx' before initialization 暂时性死区:声明前访问 let/const 确保先声明,再使用

四、补充:函数提升 vs var 提升

你的笔记中提到 "函数是一等公民",这里需要区分 函数提升var 提升 的差异:

  • 两者都会提升到作用域顶部
  • var 只提升 "声明",不提升 "赋值"(所以提前访问是 undefined)
  • 函数声明(function xxx() {})会同时提升 "声明" 和 "赋值"(所以提前调用能执行)

案例:

javascript 复制代码
// 函数提升:提前调用能执行(声明+赋值都提升了)
setWidth(); // 输出 100
function setWidth() {
  var width = 100;
  console.log(width);
}
// var 提升:提前访问是 undefined(只提升声明,不提升赋值)
console.log(getHeight); // undefined
var getHeight = function() {
  return 188;
};

五、最佳实践:该用 let 还是 const?

  1. 彻底告别 var:var 的缺陷太多,ES6 后完全没必要再用
  1. 优先用 const:只要变量的值 / 引用不需要修改,就用 const(比如函数参数、固定配置、对象 / 数组),能减少意外修改,提升代码稳定性
  1. 按需用 let:只有当变量需要重新赋值时(比如循环计数器、动态变化的状态),才用 let
  1. 冻结敏感对象:如果 const 声明的对象不希望被修改属性,记得用 Object.freeze()(深冻结需额外处理)

总结

var 是 JS 早期设计的产物,存在变量提升、无块级作用域等缺陷;let 和 const 则是 ES6 对变量声明的优化,解决了 var 的痛点,还通过 "暂时性死区" 强制规范代码逻辑。

记住核心原则:不用 var,优先 const,let 按需用,再结合对 "引用类型" 和 "块级作用域" 的理解,就能避免 99% 关于变量声明的坑~

相关推荐
Cache技术分享6 小时前
225. Java 集合 - List接口 —— 记住顺序的集合
前端·后端
前端开发爱好者6 小时前
Vite+ 获得 1250万美元的 A 轮融资,生态加速!
前端·javascript
拖拉斯旋风6 小时前
0基础学习Openai之:通过Prompt生成你心中的那幅画🎨
javascript·openai
爱抽烟的大liu6 小时前
iOS 进阶6-Voip通信
前端
boboj16 小时前
Vue过渡至React的基础理解
前端·react.js
Zyx20076 小时前
用 CSS 演绎浪漫:从零构建“亲吻动画”全流程解析
前端·css
llq_3506 小时前
在 antd Table 中实现多行省略和 Tooltip
前端
优秀员工不受影响6 小时前
如何使用 Volta
前端
my一阁6 小时前
tomcat web实测
java·前端·nginx·tomcat·负载均衡