JavaScript 变量声明:var、let 与 const
AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。
写 JavaScript 的第一件事不是学语法,而是决定「用哪个关键字声明变量」。var、let、const 行为差异很大,选错会在几周后变成难以定位的 bug。
环境准备
本系列示例均可在 Node.js 22 LTS 下直接运行。确认版本:
node -v
# v22.x.x
把示例代码保存为 demo.js,用 node demo.js 运行即可看到输出。浏览器控制台的结果与之一致(除非示例特意标注了浏览器 API)。
三种声明方式
var a = 1; // 函数作用域,会提升
let b = 2; // 块级作用域,不提升初始化
const c = 3; // 块级作用域,绑定后不可重新赋值
区别看两个维度:作用域与能否重新赋值。
if (true) {
var x = 10;
let y = 20;
}
console.log(x); // 10,var 泄漏到了块外面
console.log(y); // ReferenceError: y is not defined
var 只认函数,不认 {};let / const 认每一个块(包括 if、for、{})。这条规则几乎解释了所有「变量莫名被覆盖」的问题。
提升与暂时性死区
var 声明会被提升到函数顶部,值为 undefined,所以可以先使用后声明:
console.log(n); // undefined(不报错)
var n = 5;
let / const 也会提升,但在声明语句执行前处于暂时性死区(TDZ),访问就抛错:
console.log(m); // ReferenceError: Cannot access 'm' before initialization
let m = 5;
这个报错是好事:它把「用了还没初始化的变量」从隐性 bug 变成了显性错误。
const 绑定的是引用,不是值
const nums = [1, 2, 3];
nums.push(4); // 可以:修改对象内容
console.log(nums); // [1, 2, 3, 4]
nums = [9]; // TypeError: Assignment to constant variable
const 约束的是「变量名不能指向别的对象」,不约束对象内部。默认用 const,确实需要重新赋值时再改成 let,这是目前最主流的写法,也能顺带减少意外重赋值。
七种原始类型与对象
JavaScript 的值分两类:原始类型(不可变、按值比较)与对象(可变、按引用比较)。
| 类型 | 例子 | 说明 |
|---|---|---|
string |
"hi" |
不可变 |
number |
42、3.14 |
双精度浮点,整数小数同一种类型 |
boolean |
true |
— |
undefined |
undefined |
声明了但没赋值 |
null |
null |
表示「空」 |
symbol |
Symbol("id") |
唯一标识,初学阶段少见 |
bigint |
10n |
超过 Number 精度范围的大整数 |
object |
{}、[]、函数 |
引用类型,可变 |
用 typeof 判断类型:
typeof 42 // "number"
typeof "hi" // "string"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object"(历史遗留 bug,不是真的对象)
typeof {} // "object"
typeof [] // "object"(数组也是对象,需用 Array.isArray 判断)
typeof (() => {}) // "function"
判断数组一律用 Array.isArray(value),typeof 区分不出数组和普通对象。
undefined 与 null 的分工
undefined:系统给的「还没值」。变量声明未赋值、函数没写return、访问不存在的属性,都是undefined。null:你主动写的「这里就是空的」。
let user; // undefined,你还没给它值
let currentUser = null; // null,明确表示"当前没有用户"
判断空值最常用的是相等判断配合可选链,见本系列第 2 篇关于 ?? 的说明。
常见错误与注意事项
- 在循环里用
var声明计数器:for (var i = 0; ...)的i会泄漏到循环外,嵌套循环时互相覆盖。用let。 - 反复
var同名变量:var允许重复声明,后写的值覆盖前面的,且不报错。用let/const会直接报语法错误。 - 用
const声明后面要重新赋值的变量:会抛TypeError,改成let即可。 - 忘记声明直接赋值:
count = 1在非严格模式下会创建一个全局变量,污染全局命名空间。文件顶部加"use strict";或直接使用 ES 模块(模块默认严格模式)。 - 用
typeof x === "undefined"判断对象属性是否存在:无法区分「属性不存在」与「属性存在但值是 undefined」。需要区分时用"key" in obj或Object.hasOwn(obj, "key")。
小结
- 默认
const,需要重新赋值才用let,新代码不用var。 let/const是块级作用域,且有暂时性死区,先声明后使用。const保护绑定不改,不保护对象内容不改。- 判断数组用
Array.isArray(),typeof null是"object"这个坑要记住。