ECMAScript 2020(ES11)
Optional Chaining (?.)
安全地访问嵌套对象属性,避免因中间值为 null 或 undefined 导致的错误。
javascript
{
const user = {
name: 'Alice',
address: {
street: 'Main St',
getNum() {
return '123'
},
dataList: [
{ details: 'detail1' },
{ details: 'detail2' }
]
}
}
// 传统方式 - 冗长且易出错
const street = user && user.address && user.address.street
const num = user && user.address && user.address.getNum && user.address.getNum()
const details = user && user.address && user.address.dataList &&
user.address.dataList[0] && user.address.dataList[0].details
// 使用可选链 - 简洁安全
const street2 = user?.address?.street
const num2 = user?.address?.getNum?.()
const details2 = user?.address?.dataList?.[0]?.details
console.log(street2) // 'Main St'
console.log(num2) // '123'
console.log(details2) // 'detail1'
// 处理不存在的属性
const nonExistent = user?.profile?.avatar?.url
console.log(nonExistent) // undefined (而不是报错)
// 动态属性访问
const property = 'street'
const dynamicValue = user?.address?.[property]
console.log(dynamicValue) // 'Main St'
}
Nullish Coalescing (??)
空值合并运算符,只有当左侧为 null
或 undefined
时才使用右侧的默认值。
javascript
{
// 与 || 的区别
console.log('' || 'default') // 'default'
console.log('' ?? 'default') // ''
console.log(0 || 'default') // 'default'
console.log(0 ?? 'default') // 0
console.log(false || 'default') // 'default'
console.log(false ?? 'default') // false
console.log(null ?? 'default') // 'default'
console.log(undefined ?? 'default') // 'default'
// 实际应用场景
function processConfig(config) {
// 保留有意义的假值,只处理真正的"空值"
const timeout = config.timeout ?? 5000
const retries = config.retries ?? 3
const debug = config.debug ?? false
return { timeout, retries, debug }
}
console.log(processConfig({ timeout: 0, debug: false }))
// { timeout: 0, retries: 3, debug: false }
console.log(processConfig({ timeout: null, debug: undefined }))
// { timeout: 5000, retries: 3, debug: false }
// 与可选链结合使用
const userSettings = {
theme: null,
notifications: {
email: false,
push: undefined
}
}
const theme = userSettings?.theme ?? 'light'
const emailNotifs = userSettings?.notifications?.email ?? true
const pushNotifs = userSettings?.notifications?.push ?? true
console.log({ theme, emailNotifs, pushNotifs })
// { theme: 'light', emailNotifs: false, pushNotifs: true }
}
BigInt
新的原始数据类型,用于表示任意精度的大整数。
javascript
{
// JavaScript Number 类型的限制
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
console.log(2 ** 53) // 9007199254740992
console.log(2 ** 53 + 1) // 9007199254740992 (精度丢失!)
// BigInt 可以表示任意大的整数
const bigInt1 = 9007199254740993n
const bigInt2 = BigInt(9007199254740993)
const bigInt3 = BigInt('9007199254740994')
console.log(bigInt1) // 9007199254740993n
console.log(typeof bigInt1) // 'bigint'
// BigInt 运算
const result = 9007199254740991n + 2n
console.log(result) // 9007199254740993n
// 大数计算示例
const factorial = (n) => {
if (n <= 1n) return 1n
return n * factorial(n - 1n)
}
console.log(factorial(25n)) // 15511210043330985984000000n
// 比较操作
console.log(1n == 1) // true (类型转换)
console.log(1n === 1) // false (类型严格比较)
console.log(1n < 2) // true
console.log(2n > 1) // true
// 注意事项
// 1. 不能与 Number 混合运算
// console.log(1n + 1) // TypeError
// 2. 需要显式转换
console.log(Number(1n) + 1) // 2
console.log(1n + BigInt(1)) // 2n
// 3. BigInt 不能使用 Math 对象的方法
// console.log(Math.sqrt(4n)) // TypeError
// 4. JSON.stringify 不直接支持 BigInt
const obj = { bigNum: 123n }
// console.log(JSON.stringify(obj)) // TypeError
// 需要自定义序列化
console.log(JSON.stringify(obj, (key, value) =>
typeof value === 'bigint' ? value.toString() : value
)) // '{"bigNum":"123"}'
}
globalThis
提供跨平台访问全局对象的标准方式。
javascript
{
// 传统方式 - 平台兼容性差
function getGlobal() {
if (typeof self !== 'undefined') return self // Web Workers
if (typeof window !== 'undefined') return window // 浏览器
if (typeof global !== 'undefined') return global // Node.js
throw new Error('Unable to locate global object')
}
// 现代方式 - 统一的全局对象访问
console.log(typeof globalThis) // 'object'
// 在不同环境中都能正常工作
globalThis.myGlobalVar = 'Hello World'
console.log(globalThis.myGlobalVar) // 'Hello World'
// 实际应用:polyfill 检测
if (!globalThis.fetch) {
// 在 Node.js 环境中添加 fetch polyfill
globalThis.fetch = require('node-fetch')
}
// 检查全局 API 是否存在
if (typeof globalThis.setTimeout === 'function') {
console.log('Timer APIs available')
}
// 跨平台的全局变量设置
globalThis.APP_CONFIG = {
version: '1.0.0',
env: 'production'
}
}
Dynamic import()
动态导入模块,返回 Promise,支持按需加载。
javascript
{
// 基本用法
async function loadModule() {
try {
const module = await import('./utils.js')
module.doSomething()
} catch (error) {
console.error('Failed to load module:', error)
}
}
// 条件导入
async function loadFeature(featureName) {
const modulePath = `./features/${featureName}.js`
const module = await import(modulePath)
return module.default
}
// 与传统 import 语句的区别
// 静态导入 (编译时)
// import { helper } from './helper.js'
// 动态导入 (运行时)
const loadHelper = async () => {
const { helper } = await import('./helper.js')
return helper
}
// 实际应用:路由懒加载
const routes = {
'/home': () => import('./pages/Home.js'),
'/about': () => import('./pages/About.js'),
'/contact': () => import('./pages/Contact.js')
}
async function navigateTo(path) {
const loadPage = routes[path]
if (loadPage) {
const pageModule = await loadPage()
pageModule.default.render()
}
}
// 代码分割示例
button.addEventListener('click', async () => {
const { heavyLibrary } = await import('./heavy-library.js')
heavyLibrary.process()
})
// 动态导入 JSON
async function loadConfig() {
const config = await import('./config.json', {
assert: { type: 'json' }
})
return config.default
}
}
String.prototype.matchAll()
返回所有匹配正则表达式的迭代器,包含捕获组信息。
javascript
{
const str = `
<html>
<body>
<div>第一个div</div>
<p>这是一个p</p>
<span>span</span>
<div>第二个div</div>
</body>
</html>
`
const regExp = /<div>(.*?)<\/div>/g
// 传统方法1:使用 exec() 循环
function selectDivExec(regExp, str) {
const matches = []
let match
while ((match = regExp.exec(str)) !== null) {
matches.push(match[1])
}
return matches
}
// 传统方法2:使用 replace()
function selectDivReplace(regExp, str) {
const matches = []
str.replace(regExp, (all, first) => {
matches.push(first)
})
return matches
}
// 现代方法:使用 matchAll()
function selectDivMatchAll(regExp, str) {
const matches = []
for (const match of str.matchAll(regExp)) {
matches.push(match[1])
}
return matches
}
console.log(selectDivExec(regExp, str)) // ['第一个div', '第二个div']
console.log(selectDivReplace(regExp, str)) // ['第一个div', '第二个div']
console.log(selectDivMatchAll(regExp, str)) // ['第一个div', '第二个div']
// 更复杂的示例:解析 URL 参数
const url = 'https://example.com?name=Alice&age=25&city=NewYork&name=Bob'
const paramRegex = /([^&=]+)=([^&]*)/g
// 使用 matchAll 获取所有参数
const params = new Map()
for (const match of url.matchAll(paramRegex)) {
const [, key, value] = match
if (params.has(key)) {
// 处理重复参数
const existing = params.get(key)
params.set(key, Array.isArray(existing) ? [...existing, value] : [existing, value])
} else {
params.set(key, value)
}
}
console.log(params)
// Map(3) {
// 'name' => ['Alice', 'Bob'],
// 'age' => '25',
// 'city' => 'NewYork'
// }
// 获取匹配的详细信息
const text = 'The dates are 2023-12-25 and 2024-01-01'
const dateRegex = /(\d{4})-(\d{2})-(\d{2})/g
for (const match of text.matchAll(dateRegex)) {
console.log(`Full match: ${match[0]}`)
console.log(`Year: ${match[1]}, Month: ${match[2]}, Day: ${match[3]}`)
console.log(`Index: ${match.index}`)
console.log('---')
}
}
Promise.allSettled()
等待所有 Promise 完成(无论成功或失败),返回所有结果的状态信息。
javascript
{
// 与 Promise.all() 的对比
const promises = [
Promise.reject({ code: 500, msg: '服务异常' }),
Promise.resolve({ code: 200, data: ['1', '2', '3'] }),
Promise.resolve({ code: 200, data: ['4', '5', '6'] })
]
// Promise.all() - 任一失败就整体失败
Promise.all(promises)
.then(res => console.log('All succeeded:', res))
.catch(err => console.log('One failed:', err))
// 输出: One failed: { code: 500, msg: '服务异常' }
// Promise.allSettled() - 等待所有完成
Promise.allSettled(promises)
.then(results => {
console.log('All settled:')
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index} succeeded:`, result.value)
} else {
console.log(`Promise ${index} failed:`, result.reason)
}
})
// 过滤成功的结果
const successful = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value)
console.log('Successful results:', successful)
})
// 实际应用:批量 API 调用
async function fetchMultipleAPIs() {
const apiCalls = [
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
]
const results = await Promise.allSettled(apiCalls)
const responses = {}
results.forEach((result, index) => {
const apiName = ['users', 'posts', 'comments'][index]
if (result.status === 'fulfilled') {
responses[apiName] = result.value
} else {
console.error(`Failed to fetch ${apiName}:`, result.reason)
responses[apiName] = null
}
})
return responses
}
// 批量文件上传示例
async function uploadFiles(files) {
const uploadPromises = files.map(file =>
fetch('/upload', {
method: 'POST',
body: file
})
)
const results = await Promise.allSettled(uploadPromises)
const report = {
successful: 0,
failed: 0,
errors: []
}
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
report.successful++
} else {
report.failed++
report.errors.push({
file: files[index].name,
error: result.reason.message
})
}
})
return report
}
}