JavaScript 是一门不断发展的语言,每年都会添加新特性。了解语言的发展方向,能帮助你更好地规划学习路径和项目技术选型。
| 阶段 | 名称 | 说明 |
|---|---|---|
| Stage 0 | Strawman | 提案想法 |
| Stage 1 | Proposal | 正式提案 |
| Stage 2 | Draft | 草案规范 |
| Stage 3 | Candidate | 候选推荐 |
| Stage 4 | Finished | 等待发布 |
| 版本 | 年份 | 重要特性 |
|---|---|---|
| ES5 | 2009 | 严格模式、JSON、Object 方法 |
| ES6/ES2015 | 2015 | let/const、箭头函数、类、模块、Promise |
| ES2016 | 2016 | includes、指数运算符 |
| ES2017 | 2017 | async/await、Object.entries |
| ES2018 | 2018 | 对象展开、异步迭代 |
| ES2019 | 2019 | flat、fromEntries、可选 catch |
| ES2020 | 2020 | 可选链、空值合并、BigInt |
| ES2021 | 2021 | 逻辑赋值、replaceAll、数字分隔符 |
| ES2022 | 2022 | 顶层 await、类私有字段、at() |
| ES2023 | 2023 | 从后查找、Symbols 作为 WeakMap 键 |
| ES2024 | 2024 | 数组分组、Promise.withResolvers |
// ES2022 之前:必须在 async 函数内
async function main() {
const data = await fetch('/api/data')
return data
}
// ES2022:顶层 await
const response = await fetch('/api/data')
const data = await response.json()
console.log(data)
// 条件加载模块
const config = await (
process.env.NODE_ENV === 'production'
? import('./config.prod.js')
: import('./config.dev.js')
)
class User {
// 私有字段
#name
#age
constructor(name, age) {
this.#name = name
this.#age = age
}
// 私有方法
#validate() {
return this.#age > 0
}
// 公共方法访问私有字段
getName() {
return this.#name
}
// 静态私有字段
static #count = 0
static getCount() {
return User.#count
}
}
const user = new User('东巴文', 18)
console.log(user.getName()) // 东巴文
// console.log(user.#name) // 语法错误
class Config {
static instance
static version = '1.0.0'
// 静态初始化块
static {
try {
this.instance = new Config()
console.log('配置初始化完成')
} catch (e) {
console.error('初始化失败', e)
}
}
constructor() {
// 实例初始化
}
}
const arr = ['东', '巴', '文', '教', '程']
// 之前:获取最后一个元素
arr[arr.length - 1] // 程
// 现在:使用 at()
arr.at(-1) // 程
arr.at(-2) // 教
arr.at(0) // 东
// 字符串也支持
'东巴文'.at(-1) // 文
const user = { name: '东巴文' }
// 之前
Object.prototype.hasOwnProperty.call(user, 'name') // true
// 现在
Object.hasOwn(user, 'name') // true
Object.hasOwn(user, 'age') // false
// 更安全:不依赖原型链
const obj = Object.create(null)
obj.name = '东巴文'
// obj.hasOwnProperty('name') // 错误:没有继承 hasOwnProperty
Object.hasOwn(obj, 'name') // true
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`)
return response.json()
} catch (error) {
throw new Error(`获取用户 ${id} 失败`, { cause: error })
}
}
try {
await fetchUser(1)
} catch (error) {
console.error(error.message)
console.error(error.cause) // 原始错误
}
const arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
// 从后查找
arr.findLast(x => x > 3) // 4
arr.findLastIndex(x => x > 3) // 5
// 对比从前查找
arr.find(x => x > 3) // 4
arr.findIndex(x => x > 3) // 3
const users = [
{ name: '东巴文', role: 'admin' },
{ name: '用户A', role: 'user' },
{ name: '用户B', role: 'user' },
{ name: '用户C', role: 'guest' }
]
// Object.groupBy
const byRole = Object.groupBy(users, user => user.role)
// {
// admin: [{ name: '东巴文', role: 'admin' }],
// user: [{ name: '用户A', role: 'user' }, { name: '用户B', role: 'user' }],
// guest: [{ name: '用户C', role: 'guest' }]
// }
// Map.groupBy
const byRoleMap = Map.groupBy(users, user => user.role)
const arr = [3, 1, 4, 1, 5, 9]
// toSorted:返回新数组
const sorted = arr.toSorted()
console.log(arr) // [3, 1, 4, 1, 5, 9] 原数组不变
console.log(sorted) // [1, 1, 3, 4, 5, 9]
// toReversed
const reversed = arr.toReversed()
console.log(arr) // [3, 1, 4, 1, 5, 9]
console.log(reversed) // [9, 5, 1, 4, 1, 3]
// toSpliced
const spliced = arr.toSpliced(2, 2, 'a', 'b')
console.log(arr) // [3, 1, 4, 1, 5, 9]
console.log(spliced) // [3, 1, 'a', 'b', 5, 9]
// with:修改指定索引
const modified = arr.with(0, 'first')
console.log(arr) // [3, 1, 4, 1, 5, 9]
console.log(modified) // ['first', 1, 4, 1, 5, 9]
const weak = new WeakMap()
// 之前:只能用对象作为键
const objKey = {}
weak.set(objKey, 'value')
// 现在:可以用 Symbol
const symKey = Symbol('key')
weak.set(symKey, 'value')
console.log(weak.get(symKey)) // value
// 之前
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
// 现在
const { promise, resolve, reject } = Promise.withResolvers()
// 使用场景:事件包装
function waitForEvent(emitter, event) {
const { promise, resolve } = Promise.withResolvers()
emitter.once(event, resolve)
return promise
}
const numbers = [1, 2, 3, 4, 5, 6]
const grouped = Object.groupBy(numbers, n => n % 2 === 0 ? 'even' : 'odd')
// { odd: [1, 3, 5], even: [2, 4, 6] }
// 检查字符串是否为有效的 UTF-16
const str = '东巴文'
console.log(str.isWellFormed()) // true
const invalid = 'test\uD800'
console.log(invalid.isWellFormed()) // false
// 转换为有效的 UTF-16
const fixed = invalid.toWellFormed()
console.log(fixed.isWellFormed()) // true
// 不可变数据结构
const record = #{
name: '东巴文',
age: 18
}
const tuple = #[1, 2, 3]
// 深度比较
#{ a: 1 } === #{ a: 1 } // true
#[1, 2] === #[1, 2] // true
// 不可变更新
const updated = #{ ...record, age: 19 }
// 类似于其他语言的模式匹配
const result = match(value) {
when { type: 'success', data } => data,
when { type: 'error', message } => throw new Error(message),
when _ => 'unknown'
}
// 类装饰器
@logged
class User {
@readonly
name = '东巴文'
@validate
setAge(age) {
this.age = age
}
}
function logged(target) {
return class extends target {
constructor(...args) {
console.log('创建实例')
super(...args)
}
}
}
function readonly(target, key, descriptor) {
descriptor.writable = false
return descriptor
}
const setA = new Set([1, 2, 3])
const setB = new Set([2, 3, 4])
// 交集
setA.intersection(setB) // Set { 2, 3 }
// 并集
setA.union(setB) // Set { 1, 2, 3, 4 }
// 差集
setA.difference(setB) // Set { 1 }
// 对称差集
setA.symmetricDifference(setB) // Set { 1, 4 }
// 子集判断
setA.isSubsetOf(setB) // false
setA.isSupersetOf(setB) // false
setA.isDisjointFrom(setB) // false
// 更好的日期时间 API
const now = Temporal.Now.plainDateTimeISO()
console.log(now.toString()) // 2024-01-15T10:30:00
// 创建日期
const date = Temporal.PlainDate.from('2024-01-15')
// 时间运算
const tomorrow = date.add({ days: 1 })
const nextMonth = date.add({ months: 1 })
// 时区支持
const zoned = Temporal.ZonedDateTime.from({
year: 2024,
month: 1,
day: 15,
hour: 10,
timeZone: 'Asia/Shanghai'
})
// 持续时间
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 })
// 自定义元素
class DongbaButton extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<style>
button {
background: #1890ff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
</style>
<button><slot></slot></button>
`
}
}
customElements.define('dongba-button', DongbaButton)
// View Transitions API
document.startViewTransition(() => {
// 更新 DOM
updateContent()
})
// File System Access API
const handle = await window.showOpenFilePicker()
const file = await handle.getFile()
const contents = await file.text()
// Screen Wake Lock API
const wakeLock = await navigator.wakeLock.request('screen')
// Web Share API
await navigator.share({
title: '东巴文',
text: '分享内容',
url: 'https://db-w.cn'
})
// Clipboard API
await navigator.clipboard.writeText('复制的内容')
const text = await navigator.clipboard.readText()
// 检查特性支持
if ('groupBy' in Object) {
// 使用新特性
} else {
// 降级方案
}
// 或使用 polyfill
import 'core-js/actual/array/group-by'
// 使用 Babel 转译新语法
// 使用 core-js 提供 polyfill
// 使用特性检测保证兼容性
东巴文(db-w.cn)—— 与 JavaScript 共同成长
恭喜你完成了 JavaScript 教程的全部学习!从基础语法到高级特性,从浏览器环境到 Node.js 服务端,从代码质量到最佳实践,你已经掌握了现代 JavaScript 开发的核心知识。继续实践,不断探索,成为一名优秀的 JavaScript 开发者!