1. 语法差异与基本用法
const 与 let 是 ES6 引入的变量声明方式,相比传统的 var,它们提供了更严格的作用域与初始化约束。在实际开发中,理解它们的语法差异,是将 const 与 let 取代 var 的第一步。
块级作用域是 let/const 相较于 var 的核心特性之一。var 声明的变量属于函数作用域,可能在意料之外的地方被访问或覆盖;而 let/const 声明的变量只在大括号内可见,避免了全局污染和意外覆盖。
此外,提升(hoisting)行为也不同。var 会被提升到函数顶部,初始值为 undefined;let/const 虽然会提升,但在进入块作用域之前处于暂时性死区(TDZ),访问会抛错,促使代码在正确的顺序执行。
// var 的提升示例
function fVar() {console.log(x); // undefinedvar x = 1;
}
fVar();// let 的 TDZ 示例
function fLet() {// console.log(y); // ReferenceError: y is not definedlet y = 2;
}
fLet();1.1 const 与 let 的声明差异
const 必须初始化,且绑定不可重新赋值,但对于引用类型而言,对象属性仍然可以改变;这意味着“不可变”的是变量绑定本身,而非值的内部内容。
let 可以按需重新赋值,但同样具备块级作用域,适合可变状态的变量。对于需要不可变状态的场景,使用 const 能更好地表达设计意图。
下面的对比展示了两者在基本用法上的差异:const 适合常量、不可重新赋值的绑定;let 适合循环变量、状态变量等需要重新赋值的场景。
1.2 赋值与不可变性的误解
将 const 应用于对象或数组 时,绑定不可重新赋值,但对象的成员可以修改、添加或删除。这个细微差异常被误解为“完全不可变”,实际情况如下所示:

const arr = [1, 2, 3];
arr.push(4); // 允许修改数组内容
console.log(arr); // [1, 2, 3, 4]const obj = { a: 1 };
obj.a = 2; // 允许修改对象属性
console.log(obj); // { a: 2 }// 但重新赋值会报错
// arr = [5, 6]; // TypeError: Assignment to constant variable.
// obj = {}; // TypeError: Assignment to constant variable.2. 兼容性与工具链
2.1 浏览器与 Node 的 ES6+ 支持
现代浏览器对 let、const 的原生支持非常广泛,包括 Chromium、Firefox、Edge、Safari 等主流内核的版本。对于需要支持旧版浏览器的场景,构建阶段的转译与打包非常关键。
在服务器端,Node.js 的较新版本原生支持 let/const,但若项目还存在对较 old 的运行环境需求,仍需通过转译实现一致性。了解目标环境的最低版本,是决定是否需要 Babel 转译的关键。
为了实现跨环境一致性,通常需要结合工具链进行处理:编译、打包与降级目标的设定,确保在老旧环境中也能正确执行。
2.2 转译与打包流程
实现对 const、let 与 var 的统一处理,常用做法是通过 Babel 等转译工具将 ES6 语法降级到目标环境兼容的版本。核心思路包括:设置目标环境、按需转译、以及在必要时引入 polyfills。
下面是一个常见的 Babel 配置片段,使用 preset-env 来覆盖目标浏览器与 Node 版本的需要:
// babel.config.js
module.exports = {presets: [["@babel/preset-env", {"targets": "> 0.25%, not dead","useBuiltIns": "entry","corejs": 3}]]
};3. 项目迁移实操
3.1 评估与准备
在正式迁移前,进行全面评估,明确哪些地方可以直接替换,哪些需要重构。通过静态分析与测试用例,识别 var 的使用模式、循环变量、闭包场景等潜在风险点。
推荐使用代码质量工具来支撑迁移计划:eslint、no-var、prefer-const、no-redeclare 等规则可以帮助快速定位问题。
3.2 逐步替换策略
迁移通常采取渐进式策略,先将 不可变状态的变量 使用 const;随后将需要重新赋值的变量替换为 let。对于变量作用域较复杂的情况,先通过测试覆盖后再进行替换。
以下是一个简单的替换示例,展示如何从 var 向 let/const 演进:先分析赋值行为,再决定使用 let 还是 const,避免一次性全量替换导致的逻辑错乱。
// 原始代码(使用 var)
function count() {for (var i = 0; i < 3; i++) {setTimeout(function() {console.log(i);}, 0);}
}// 替换方案(逐步)
function countFixed() {for (let i = 0; i < 3; i++) {setTimeout(function() {console.log(i);}, 0);}
}// 对不可变绑定使用 const 的示例
function createPoint() {const origin = { x: 0, y: 0 };// origin 绑定不可重新赋值,但对象内容可变origin.x = 5;return origin;
}4. 常见坑与优化
4.1 循环中的闭包坑与解决方案
在循环内部使用 var 声明的变量,容易造成闭包引用同一份变量导致输出不符预期。使用 let来绑定每次迭代的独立变量,是最直接的解决方案。
示例对比:let 带来的分离绑定可确保每次输出的值与循环索引一致。
// var 的常见坑
for (var i = 0; i < 3; i++) {setTimeout(function() { console.log(i); }, 0);
}
// 输出: 3, 3, 3// 替换为 let
for (let i = 0; i < 3; i++) {setTimeout(function() { console.log(i); }, 0);
}
// 输出: 0, 1, 24.2 尽量使用静态分析与测试驱动迁移
在迁移过程中,强制执行静态分析和单元测试,能显著降低回归风险。通过开启 prefer-const、no-var 等规则,可以快速识别需要尝试替换的变量。
另外,结合持续集成(CI)流水线,在每次变更后运行测试用例,确保对 var 到 let/const 的替换保持行为一致性。


