2015 年 6 月,ECMAScript 6(简称 ES6,官方名称 ES2015)正式发布,这是 JavaScript 语言诞生以来规模最大的一次更新,甚至可以说,它把 JavaScript 从"脚本语言"推向了"工程语言"。在此之前,var 的函数作用域陷阱、层层嵌套的回调、缺乏模块系统等问题,让大型前端项目举步维艰。ES6 一口气带来了块级作用域、箭头函数、解构赋值、模板字符串和原生模块等特性,如今它们已是所有现代框架和 Node.js 项目的共同语法基础。顺便说一句,ES6 与 ES2015 是同一个东西:前者是流传更广的宣传名,后者是按年份编号的官方名,本文混用不区分。
1. let 与 const:告别 var 的坑
在 ES6 之前,声明变量只有 var 一种方式,而它有两个著名的问题:一是函数作用域而非块级作用域,循环里声明的变量在循环外依然可见;二是变量提升,导致"先使用、后声明"也能运行,容易埋下难以察觉的隐患。let 与 const 把作用域收窄到所在的花括号内,并引入"暂时性死区"(TDZ):在声明语句之前访问变量会直接抛出 ReferenceError,把错误提前暴露出来。const 更进一步,声明时必须赋值且之后不能再重新赋值,适合表达"这个值不会变"的意图。注意,const 冻结的只是变量与值的"绑定",如果值是对象,对象的属性仍然可以修改。
// var:函数作用域 + 变量提升
function demo() {
console.log(a); // undefined,不会报错
var a = 1;
}
// let:块级作用域
{
let x = 1;
console.log(x); // 1
}
// console.log(x); // ReferenceError:x 不在作用域内
// const:声明时必须赋值,且不能重新赋值
const PI = 3.14;
// PI = 3; // TypeError:Assignment to constant variable
// 但 const 对象的内容可以修改
const user = { name: "小明" };
user.name = "小红"; // 合法
| 声明方式 | 作用域 | 变量提升 | 可重复声明 | 可重新赋值 |
|---|---|---|---|---|
| var | 函数 | 是(得到 undefined) | 是 | 是 |
| let | 块 | 否(TDZ 报错) | 否 | 是 |
| const | 块 | 否(TDZ 报错) | 否 | 否 |
2. 箭头函数:更简洁,this 更省心
箭头函数用 => 取代 function 关键字,写法紧凑;更关键的是它没有自己的 this,会继承定义时所在作用域的 this。以前回调函数里 this 丢失,得用 that = this 或 bind 兜底,箭头函数让这类问题基本消失。代价有两个:箭头函数不能用作构造函数(用 new 调用会抛错),也没有自己的 arguments 对象(可以用 rest 参数替代)。判断规则很简单:需要动态 this 的场景(对象方法、构造函数)用普通函数,其余场景放心用箭头函数。另外,箭头函数也没有 prototype 属性,这进一步印证了它不适合作为构造函数。
// 基本写法:多参数 + 单条表达式
const add = (a, b) => a + b;
// 单个参数可省略括号,单条语句可省略 return
const double = n => n * 2;
// 返回对象字面量必须加括号
const createUser = name => ({ name });
// this 的差异:箭头函数继承外层 this
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++; // 这里的 this 指向 Timer 实例
}, 1000);
}
3. 解构赋值:按模式批量取值
解构可以理解为"按照模式批量取值"。数组解构按位置取,对象解构按属性名取,两者都支持默认值,还能用 ...rest 收集剩余项。最常见的两个用法:一是从接口返回的对象中一次性取出需要的字段,代码立刻短一大截;二是函数参数直接解构,调用时不用再纠结参数顺序。配合默认参数,可选字段的语义一目了然。嵌套对象也可以逐层解构,不过嵌套太深时建议拆开写,保持可读性。解构还有一个经典用法:交换两个变量的值,[a, b] = [b, a] 一行搞定,比引入临时变量直观。
// 数组解构:按位置取值
const [first, second, ...rest] = [10, 20, 30, 40];
// first = 10,second = 20,rest = [30, 40]
// 对象解构:按属性名取值,可重命名、设默认值
const user = { id: 1, name: "小明" };
const { id, name: userName, age = 18 } = user;
// id = 1,userName = "小明",age = 18
// 函数参数解构
function render({ title, width = 100 }) {
console.log(title, width);
}
render({ title: "Hello" }); // Hello 100
4. 模板字符串与默认参数
模板字符串用反引号包裹,支持换行和 ${} 插值,拼 HTML、拼 SQL、拼路径时比字符串拼接干净得多,也彻底告别了转义引号的痛苦。默认参数让"可选参数"有了标准写法:传入 undefined 时自动使用默认值,而且默认值可以引用前面的参数。
// 模板字符串:支持换行与插值
const name = "CodeLab";
const html = `
<h1>欢迎来到 ${name}</h1>
<p>今天共有 ${3 + 4} 篇文章</p>
`;
// 默认参数
function greet(name, prefix = "你好") {
return `${prefix},${name}`;
}
greet("小明"); // 你好,小明
greet("小明", "Hi"); // Hi,小明
5. 模块系统:import 与 export
ES6 模块(ESM)是 JavaScript 官方第一次提供模块能力。它用 export 导出、import 导入,支持命名导出与默认导出。与 Node.js 早期使用的 CommonJS 相比,ESM 是静态的:import 语句出现在顶层、模块名是字面量,编译器可以在运行前分析依赖关系,从而做 tree shaking(摇树优化),打包时剔除未使用的代码。使用时要留意加载方式:浏览器通过 <script type="module"> 加载,Node.js 里把文件后缀改成 .mjs,或在 package.json 中声明 "type": "module"。如果只是个人练习,CommonJS 在 Node 里依然完全可用,但新项目建议直接上 ESM——它也是 Deno、Bun 等新一代运行时唯一推荐的模块方式。
// math.js:命名导出 + 默认导出
export const PI = 3.14159;
export function square(x) {
return x * x;
}
export default class Calculator {}
// main.js:混合导入
import Calculator, { PI, square } from "./math.js";
console.log(square(PI)); // 9.8696...
6. 如何在你的环境里启用
ES6 的语法特性(let/const、箭头函数、解构、模板字符串)在 2017 年之后发布的所有主流浏览器中都原生支持;Node.js 从 6.x 开始逐步支持,12.x 起完整支持,现在的 LTS 版本(18/20/22)更是不在话下。真正需要注意的只有 ES 模块:浏览器需要 <script type="module">,Node.js 需要 "type": "module" 或 .mjs 后缀。如果项目还要兼容 IE 等上古浏览器,可以引入 Babel 把 ES6 转译成 ES5,但到 2026 年的今天,这种需求已经很少见了。如果拿不准某个特性在目标环境是否可用,可以查 caniuse.com,或直接在 Node 里跑一段验证代码;语法级的问题用 node --check 就能提前发现。
| 特性 | 浏览器支持起点 | Node.js 支持起点 |
|---|---|---|
| let / const | Chrome 49+ | 4+ |
| 箭头函数 | Chrome 45+ | 4+ |
| 解构赋值 | Chrome 49+ | 6+ |
| 模板字符串 | Chrome 41+ | 4+ |
| ES 模块 | Chrome 61+ | 12+(完整) |
7. 值不值得用
这个问题没有悬念:不是"值不值得",而是"必须用"。ES6 是现代 JavaScript 的最小公约数,几乎所有框架、工具链和面试题都建立在它之上。具体建议:把 var 全面替换为 let/const,新代码习惯性使用箭头函数、解构与模板字符串;模块系统则应该成为所有新项目组织代码的默认方式。唯一要克制的是,不要为了炫技把简单代码写成复杂的解构嵌套——可读性永远是第一位的。
💡 学习建议:不要只看不练。打开浏览器控制台或 Node.js REPL,把文中的例子亲手敲一遍;再翻出一段自己写的旧代码,用 let/const、箭头函数、解构、模板字符串重写一遍,对比可读性的变化,比读十遍教程都有效。