《变量与作用域:var / let / const 到底怎么选?》

写 JS 时用 varlet 还是 const?很多人要么凭感觉,要么"一律用 const"。这篇文章不讲特别玄的底层,只讲三件事:基础概念别混、日常怎么选、坑在哪。适合:已经会写 JS 但概念有点混的、从零开始的小白、以及想打牢基础、校准习惯的前端。

一、先搞清楚:三个关键字分别是什么

1.1 一句话区别

关键字 出现时间 作用域 能否重复声明 能否先使用再声明
var ES5 函数作用域 可以 可以(会提升)
let ES6 块级作用域 不可以 不可以(暂时性死区)
const ES6 块级作用域 不可以 不可以(暂时性死区)

用人话说:

  • var:老写法,按"函数"划分地盘,容易踩坑。
  • let:按"块"划分地盘,不能重复声明,更符合直觉。
  • const:和 let 一样是块级,但声明后不能重新赋值(注意:引用类型里的属性可以改)。

1.2 作用域:函数作用域 vs 块级作用域

函数作用域(var): 只认 function,不认 if/for/while 等块。

javascript 复制代码
function fn() {
  if (true) {
    var a = 1;
  }
  console.log(a);  // 1 ------ if 块挡不住 var
}

块级作用域(let/const):{},包括 ifforwhile、单独 {}

javascript 复制代码
function fn() {
  if (true) {
    let a = 1;
    const b = 2;
  }
  console.log(a);  // ReferenceError: a is not defined
  console.log(b);  // ReferenceError: b is not defined
}

日常结论: 在块里声明的变量,如果希望"只在这个块里有效",用 let/const;用 var 会"漏"到整个函数,容易产生隐蔽 bug。

1.3 变量提升(Hoisting)/ˈhɔɪstɪŋ/ 与暂时性死区(TDZ)

ps· TDZ全称:Temporal Dead Zone 音标:/ˈtempərəl/, /ded/ ,/zəʊn/

var:会提升,先使用再声明也不会报错(只是值为 undefined

javascript 复制代码
console.log(x);  // undefined
var x = 10;
console.log(x);  // 10

let/const:有暂时性死区,在声明之前访问会报错

javascript 复制代码
console.log(y);  // ReferenceError: Cannot access 'y' before initialization
let y = 10;

日常结论: 养成"先声明、再使用"的习惯,用 let/const 可以避免"还没赋值就被用"的坑。

1.4 const 不是"完全不能改"

const 限制的是绑定 (不能重新赋值),不限制引用类型内部的修改

javascript 复制代码
const obj = { name: '小明' };
obj.name = '小红';   // ✅ 可以,改的是对象内部
obj = {};            // ❌ 报错,不能换一个对象

const arr = [1, 2, 3];
arr.push(4);         // ✅ 可以
arr = [];            // ❌ 报错

所以:const 适合"这个变量指向的引用不变"的场景,不是"对象/数组内容不能动"。

二、日常写代码:到底怎么选?

2.1 推荐原则(可直接当规范用)

  1. 默认用 const

    只要这个变量不会在逻辑里被重新赋值 ,就用 const。包括:对象、数组、函数、配置、导入的模块等。

  2. 需要"会变"的变量用 let

    例如:循环计数器、会随逻辑重新赋值的中间变量、交换两数等。

  3. 新代码里不用 var

    除非维护老项目且项目约定用 var,否则一律 let/const

2.2 按场景选

场景 推荐 原因
导入模块、配置对象、API 地址等 const 不打算换引用
普通对象、数组(内容会增删改) const 引用不变,只改内部
for 循环里的下标 / 循环变量 let 每次迭代会变
需要先声明、后面再赋值的变量 let const 声明时必须赋初值
交换变量、累加器、临时中间变量 let 会重新赋值
老项目、历史代码 按项目规范,能改则逐步改为 let/const 避免混用加重混乱

2.3 简单示例

javascript 复制代码
// ✅ 用 const:引用不变
const API_BASE = 'https://api.example.com';
const user = { name: '张三', age: 25 };
user.age = 26;  // 可以

// ✅ 用 let:会重新赋值
let count = 0;
count++;
let temp;
if (condition) temp = a; else temp = b;

// ❌ 不要用 var(新代码)
var oldStyle = 1;  // 容易漏出块、提升导致误用

三、常见坑:会踩在哪?

3.1 坑一:循环里用 var,回调里拿到的是"最后的那个值"

javascript 复制代码
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:3, 3, 3(共用一个 i,循环结束后 i 已是 3)

正确写法:let,每次迭代都是新的绑定。

javascript 复制代码
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:0, 1, 2

3.2 坑二:同一作用域里重复声明 let/const 会报错

javascript 复制代码
let a = 1;
let a = 2;  // SyntaxError: Identifier 'a' has already been declared

var 可以重复声明(不报错),但可读性和维护性差。用 let/const 可以尽早发现"名字写重了"的问题。

3.3 坑三:const 声明时必须赋初值

javascript 复制代码
const x;  // SyntaxError: Missing initializer in const declaration
const y = 1;  // ✅

如果"现在不知道值,后面才赋值",用 let

3.4 坑四:以为 const 对象/数组"完全不能改"

再次强调:const 限制的是「变量与引用类型的绑定关系」(变量不能指向新的引用地址),而非对象的属性值 / 数组的元素值。我们可以修改的是 "引用类型内部的内容",比如对象的value、数组的元素。

3.5 坑五:老项目里 varlet/const混用

同一函数里既有 var 又有 let,作用域和提升行为不一致,排查问题会很难。建议:新加的逻辑一律 let/const,老代码有机会就逐步替换成 let/const

四、和"作用域"相关的两个小点

4.1 块级作用域对 if/else 很有用

javascript 复制代码
if (condition) {
  const message = 'yes';
  // 只用在这里
} else {
  const message = 'no';
  // 只用在这里
}
// message 在块外不可见,不污染外部

var 的话,message 会跑到整个函数里,容易重名或误用。

4.2 模块、全局与 window

  • ES Module 里,顶层的 const/let 不会挂到 window 上,和"全局变量"是两回事。
  • 传统脚本 里,顶层 var 会变成 window 的属性。
  • 日常:用模块 + const/let,减少全局污染。

五、总结:一张表 + 一句话

要点 说明
默认 能用 const 就用 const
会重新赋值 let
新项目/新代码 不用 var
循环 + 异步/回调 let,避免 var 的"最后一个值"
const 不能重新赋值,但对象/数组内部可以改

一句话: 日常写 JS,默认 const,要改再用 let,别再写 var。先把"选谁"的习惯固定下来,再结合作用域和 TDZ 理解"为什么",就能少踩坑、代码也更清晰。

以上就是本次的学习分享,欢迎大家在评论区讨论指正,与大家共勉。

我是 Eugene,你的电子学友。

如果文章对你有帮助,别忘了点赞、收藏、加关注,你的认可是我持续输出的最大动力~

相关推荐
mCell4 小时前
如何零成本搭建个人站点
前端·程序员·github
mCell5 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
恋猫de小郭5 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清5 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
萧曵 丶5 小时前
Vue 中父子组件之间最常用的业务交互场景
javascript·vue.js·交互
银烛木5 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_607076605 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声5 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易5 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得06 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化