JavaScript 变量声明:var、let 与 const

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

Node.js 22 LTS

写 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 篇关于 ?? 的说明。

常见错误与注意事项

  1. 在循环里用 var 声明计数器:for (var i = 0; ...) 的 i 会泄漏到循环外,嵌套循环时互相覆盖。用 let。
  2. 反复 var 同名变量:var 允许重复声明,后写的值覆盖前面的,且不报错。用 let / const 会直接报语法错误。
  3. 用 const 声明后面要重新赋值的变量:会抛 TypeError,改成 let 即可。
  4. 忘记声明直接赋值:count = 1 在非严格模式下会创建一个全局变量,污染全局命名空间。文件顶部加 "use strict"; 或直接使用 ES 模块(模块默认严格模式)。
  5. 用 typeof x === "undefined" 判断对象属性是否存在:无法区分「属性不存在」与「属性存在但值是 undefined」。需要区分时用 "key" in obj 或 Object.hasOwn(obj, "key")。

小结

  • 默认 const,需要重新赋值才用 let,新代码不用 var。
  • let / const 是块级作用域,且有暂时性死区,先声明后使用。
  • const 保护绑定不改,不保护对象内容不改。
  • 判断数组用 Array.isArray(),typeof null 是 "object" 这个坑要记住。

下一步