ES6+ 核心语法速通
为什么要先学 ES6+
ES6 (ES2015) 是 JavaScript 的现代化起点。在那之前的 JS 写起来像玩具语言,之后才真正具备了写大型应用的能力。Vue 3 的源码、TypeScript、以及几乎所有现代前端库,都大量使用 ES6+ 语法。
Java 类比:ES6 之于 JS,大概相当于 Java 8 的 Lambda + Stream + Optional 那一波更新——都是让语言从"能用"变成"好用"的分水岭。
1. 变量声明:let / const 取代 var
// 旧写法(少碰)
var name = 'foo'
// 新写法
let age = 18 // 可变
const MAX = 100 // 不可变(但对象里的属性还是能改)
Java 对照:
| Java | JS |
|---|---|
int age = 18; |
let age = 18 |
final int MAX = 100; |
const MAX = 100 |
坑点:
const锁的是"引用不能变",对象内部的属性仍然能改。这和 Java 的final完全一致。const user = { name: 'foo' } user.name = 'bar' // ✅ 可以 user = { name: 'x' } // ❌ 报错var有变量提升和函数作用域的怪异行为,新代码永远用let/const。
2. 箭头函数:轻量级 Lambda
// 普通函数
function add(a, b) {
return a + b
}
// 箭头函数
const add = (a, b) => a + b
Java 对照:
// Java 8 Lambda
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
关键差异:Java 的 Lambda 里 this 指向外部类;JS 的箭头函数里 this 也指向外层作用域。但普通 function 里的 this 是个坑——谁调用它就是谁,和 Java 完全不一样。
const obj = {
count: 0,
// ❌ 这里不要用箭头函数,this 会指向全局而非 obj
bad: () => { this.count++ },
// ✅ 方法简写,this 正确指向 obj
good() { this.count++ },
}
建议:回调函数用箭头函数,对象方法用简写。
3. 解构赋值:一次取多个值
const user = { name: 'foo', age: 18, email: 'f@x.com' }
// 对象解构
const { name, age } = user
// 等价于 const name = user.name; const age = user.age;
// 数组解构
const [first, second] = [10, 20]
// 重命名 + 默认值
const { name: userName, role = 'guest' } = user
Java 对照:Java 没有解构。在 Java 里你得写:
String name = user.getName();
int age = user.getAge();
常见用途:函数参数直接解构,等价于 Java 的命名参数。
function createUser({ name, age, role = 'user' }) {
// ...
}
createUser({ name: 'foo', age: 18 })
4. 模板字符串:告别字符串拼接
const name = 'foo'
const greeting = `Hello, ${name}! Today is ${new Date().toLocaleDateString()}.`
// 多行字符串也很自然
const html = `
<div>
<h1>${title}</h1>
</div>
`
Java 对照:类似 Java 15 的文本块 """...""",加上 String.format() 或 Java 21 的 STR."Hello \{name}" 模板。
5. 扩展运算符 (...)
// 数组
const arr1 = [1, 2, 3]
const arr2 = [0, ...arr1, 4] // [0, 1, 2, 3, 4]
// 对象合并
const base = { a: 1, b: 2 }
const extended = { ...base, c: 3 } // { a: 1, b: 2, c: 3 }
// 函数参数收集
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0)
}
sum(1, 2, 3, 4) // 10
Java 对照:
- 收集参数
...nums≈ Java 的可变参数int... nums - 对象合并 ≈
new HashMap<>(base) { { put("c", 3); } },但 JS 优雅得多
注意:对象扩展是浅拷贝。嵌套对象还是引用。
6. Promise 与 async/await:告别回调地狱
这是 ES6+ 最重要的更新之一。前端几乎所有异步操作(网络请求、定时器、文件读写)都用它。
// 用 Promise
fetch('/api/user')
.then(res => res.json())
.then(user => console.log(user))
.catch(err => console.error(err))
// 用 async/await(推荐)
async function loadUser() {
try {
const res = await fetch('/api/user')
const user = await res.json()
console.log(user)
} catch (err) {
console.error(err)
}
}
Java 对照:
| Java | JS |
|---|---|
CompletableFuture<T> |
Promise<T> |
.thenApply(fn) |
.then(fn) |
.exceptionally(fn) |
.catch(fn) |
| Java 19+ 虚拟线程让异步代码看起来同步 | async/await |
关键理解:
async函数必然返回Promise,哪怕你 return 的是普通值await只能用在async函数里,它会"暂停"当前函数直到 Promise 完成await并不会阻塞线程——JS 是单线程的,它只是让出执行权给事件循环
常见坑:忘记 await 导致拿到的是 Promise 对象而不是结果值。
7. 模块化:import / export
// user.js
export function getUser() { /* ... */ }
export const USER_ROLE = 'admin'
export default class UserService { /* ... */ }
// main.js
import UserService, { getUser, USER_ROLE } from './user.js'
Java 对照:
export≈ Java 的public- 默认导出
export default≈ 一个文件的"主类",在 Java 里对应文件名同名的 public class import≈ Java 的import,但 JS 可以具名导入、重命名、全部导入
import * as UserModule from './user.js' // 全部导入成命名空间
import { getUser as fetchUser } from './user.js' // 重命名
8. 可选链 (?.) 与空值合并 (??)
这是 ES2020 加进来的,写 JS 和 TS 时非常常用。
// 可选链:避免 TypeError
const city = user?.address?.city // 任何一层是 null/undefined 就返回 undefined
// 空值合并:只在 null/undefined 时用默认值
const name = user.name ?? '匿名'
// 注意:|| 会把 '' 和 0 也当"假值"处理,?? 只看 null/undefined
Java 对照:
?.≈Optional.map(...).orElse(null),但语法优雅得多??≈Objects.requireNonNullElse(name, "匿名")
9. 数组高阶方法:函数式风格
const nums = [1, 2, 3, 4, 5]
nums.map(n => n * 2) // [2, 4, 6, 8, 10]
nums.filter(n => n % 2 === 0) // [2, 4]
nums.reduce((sum, n) => sum + n, 0) // 15
nums.find(n => n > 3) // 4
nums.some(n => n > 4) // true
nums.every(n => n > 0) // true
Java 对照:几乎一一对应 Java 8 Stream API。
nums.stream().map(n -> n * 2).toList();
nums.stream().filter(n -> n % 2 == 0).toList();
nums.stream().reduce(0, (a, b) -> a + b);
差异:
- JS 这些方法直接定义在数组上,不用先
.stream() - JS 的
map/filter返回新数组,不修改原数组,和 Stream 一致
小结:速查表
| 特性 | 一句话 |
|---|---|
let / const |
取代 var,块级作用域 |
箭头函数 => |
短函数 + 绑定外层 this |
解构 { a, b } = obj |
批量取值 |
模板字符串 ` ` |
拼接字符串 + 多行 |
扩展运算符 ... |
展开数组/对象、收集参数 |
async / await |
写起来像同步的异步 |
import / export |
模块化 |
?. / ?? |
空值安全 |
| 数组高阶方法 | 函数式风格,类似 Stream |
延伸阅读
- MDN: JavaScript 指南
- 阮一峰《ECMAScript 6 入门》 — 最权威的中文 ES6 教程
- You Don't Know JS (2nd Edition) — 深入原理推荐这本
下一篇:TypeScript 基础(从 Java 视角看 TS 的类型系统)