ECMAScript 2018(ES9)
RegExp Features
dotAll flag (s)
s
标志使 .
能够匹配任何字符,包括换行符。
javascript
{
// 传统情况下,. 不能匹配换行符
console.log(/foo.bar/.test('fooabar')) // true
console.log(/foo.bar/.test('foo\nbar')) // false
// 使用 dotAll 标志
console.log(/foo.bar/s.test('foo\nbar')) // true
// 检查标志
const regex = /foo.bar/gisu
console.log(regex.dotAll) // true
console.log(regex.flags) // 'gisu'
// 实际应用:多行文本解析
const htmlContent = `
<div class="header">
Title
</div>
`
// 匹配跨行的 HTML 标签
const tagRegex = /<div.*?>.*?<\/div>/s
console.log(tagRegex.test(htmlContent)) // true
// 解析配置文件中的多行值
const config = `
name = MyApp
description = This is a long
description that spans
multiple lines
version = 1.0.0
`
const multiLineValue = /description\s*=\s*(.*?)(?=\n\w+\s*=|\n*$)/s
const match = config.match(multiLineValue)
if (match) {
console.log(match[1].trim().replace(/\s+/g, ' '))
// 'This is a long description that spans multiple lines'
}
}
Named Capture Groups
使用命名捕获组让正则表达式更可读和可维护。
javascript
{
// 传统的数字索引方式
const dateMatch = '2020-03-28'.match(/(\d{4})-(\d{2})-(\d{2})/)
console.log(dateMatch[1]) // '2020'
console.log(dateMatch[2]) // '03'
console.log(dateMatch[3]) // '28'
// 命名捕获组
const namedMatch = '2020-03-28'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/)
console.log(namedMatch.groups.year) // '2020'
console.log(namedMatch.groups.month) // '03'
console.log(namedMatch.groups.day) // '28'
// 实际应用:解析电子邮件
const email = 'john.doe@example.com'
const emailRegex = /(?<username>[^@]+)@(?<domain>[^.]+)\.(?<tld>.+)/
const emailMatch = email.match(emailRegex)
if (emailMatch) {
const { username, domain, tld } = emailMatch.groups
console.log({ username, domain, tld })
// { username: 'john.doe', domain: 'example', tld: 'com' }
}
// 解析 URL
const url = 'https://api.github.com/users/octocat'
const urlRegex = /(?<protocol>https?):\/\/(?<host>[^\/]+)(?<path>\/.*)?/
const urlMatch = url.match(urlRegex)
if (urlMatch) {
console.log(urlMatch.groups)
// { protocol: 'https', host: 'api.github.com', path: '/users/octocat' }
}
// 在替换中使用命名组
const text = 'Call me at 555-123-4567'
const phoneRegex = /(?<area>\d{3})-(?<exchange>\d{3})-(?<number>\d{4})/
const formatted = text.replace(phoneRegex, '($<area>) $<exchange>-$<number>')
console.log(formatted) // 'Call me at (555) 123-4567'
// 解构赋值与命名组
function parseLogEntry(logLine) {
const logRegex = /(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>\w+)\] (?<message>.*)/
const match = logLine.match(logRegex)
if (match) {
return match.groups
}
return null
}
const log = '2023-12-25 10:30:45 [ERROR] Database connection failed'
console.log(parseLogEntry(log))
// { timestamp: '2023-12-25 10:30:45', level: 'ERROR', message: 'Database connection failed' }
}
Lookbehind Assertions
正向和负向回顾断言,检查某个位置之前的内容。
javascript
{
const text = 'hello world'
// 正向前瞻 (?=) - 匹配后面跟着指定内容的位置
console.log(text.match(/hello(?=\sworld)/)) // ['hello']
// 正向回顾 (?<=) - 匹配前面是指定内容的位置
console.log(text.match(/(?<=hello\s)world/)) // ['world']
// 负向回顾 (?<!) - 匹配前面不是指定内容的位置
console.log(text.match(/(?<!helle\s)world/)) // ['world']
// 实际应用:价格解析
const prices = '$100 €200 ¥300 100USD'
// 匹配前面有货币符号的数字
const withSymbol = prices.match(/(?<=[$€¥])\d+/g)
console.log(withSymbol) // ['100', '200', '300']
// 匹配后面有货币代码的数字
const withCode = prices.match(/\d+(?=[A-Z]{3})/g)
console.log(withCode) // ['100']
// 匹配不在引号内的单词
const code = 'const message = "hello world"; const name = "Alice"'
const outsideQuotes = code.match(/(?<!")(?<!\w)\w+(?=\s*=)/g)
console.log(outsideQuotes) // ['const', 'const']
// 密码验证:至少8位,包含大小写字母和数字
function validatePassword(password) {
const hasLower = /(?=.*[a-z])/.test(password)
const hasUpper = /(?=.*[A-Z])/.test(password)
const hasDigit = /(?=.*\d)/.test(password)
const isLongEnough = password.length >= 8
return hasLower && hasUpper && hasDigit && isLongEnough
}
console.log(validatePassword('Password123')) // true
console.log(validatePassword('password')) // false
// 提取 HTML 标签中的属性值
const html = '<img src="image.jpg" alt="A beautiful sunset" width="300">'
const attributeRegex = /(?<=\w+=")[^"]+(?=")/g
const attributes = html.match(attributeRegex)
console.log(attributes) // ['image.jpg', 'A beautiful sunset', '300']
}
Asynchronous Iteration
for-await-of
异步迭代器,用于处理异步可迭代对象。
javascript
{
// 创建异步生成器
async function* asyncGenerator() {
yield await Promise.resolve(1)
yield await Promise.resolve(2)
yield await Promise.resolve(3)
}
// 使用 for-await-of 循环
async function consumeAsyncGenerator() {
for await (const value of asyncGenerator()) {
console.log(value) // 1, 2, 3 (按顺序输出)
}
}
// 实际应用:处理异步数据流
function createAsyncIterable(urls) {
return {
[Symbol.asyncIterator]() {
let index = 0
return {
async next() {
if (index < urls.length) {
try {
const response = await fetch(urls[index++])
const data = await response.json()
return { value: data, done: false }
} catch (error) {
return { value: error, done: false }
}
} else {
return { done: true }
}
}
}
}
}
}
// 使用示例
async function fetchMultipleAPIs() {
const urls = ['/api/users', '/api/posts', '/api/comments']
const asyncIterable = createAsyncIterable(urls)
for await (const result of asyncIterable) {
if (result instanceof Error) {
console.error('Failed to fetch:', result.message)
} else {
console.log('Fetched data:', result)
}
}
}
// 自定义异步迭代器示例
const timeoutIterable = {
[Symbol.asyncIterator]() {
let count = 0
return {
async next() {
if (count < 3) {
await new Promise(resolve => setTimeout(resolve, 1000))
return { value: `Item ${++count}`, done: false }
}
return { done: true }
}
}
}
}
async function processTimeouts() {
console.log('Starting async iteration...')
for await (const item of timeoutIterable) {
console.log(item) // 每秒输出一次
}
console.log('Completed!')
}
// 处理 ReadableStream
async function processStream(stream) {
const reader = stream.getReader()
try {
for await (const chunk of readableStreamAsyncIterable(reader)) {
console.log('Chunk:', chunk)
}
} finally {
reader.releaseLock()
}
}
async function* readableStreamAsyncIterable(reader) {
try {
while (true) {
const { done, value } = await reader.read()
if (done) return
yield value
}
} finally {
reader.releaseLock()
}
}
}
Object Rest/Spread Properties
对象的剩余/扩展属性,类似于数组的扩展语法。
javascript
{
// 对象扩展
const obj1 = { a: 1, b: 2 }
const obj2 = { c: 3, d: 4 }
const combined = { ...obj1, ...obj2, e: 5 }
console.log(combined) // { a: 1, b: 2, c: 3, d: 4, e: 5 }
// 对象剩余属性
const person = {
name: 'Alice',
age: 30,
city: 'New York',
country: 'USA',
occupation: 'Developer'
}
const { name, age, ...rest } = person
console.log(name) // 'Alice'
console.log(age) // 30
console.log(rest) // { city: 'New York', country: 'USA', occupation: 'Developer' }
// 实际应用:配置对象合并
const defaultConfig = {
theme: 'light',
language: 'en',
notifications: true,
autoSave: false
}
const userConfig = {
theme: 'dark',
notifications: false
}
const finalConfig = { ...defaultConfig, ...userConfig }
console.log(finalConfig)
// { theme: 'dark', language: 'en', notifications: false, autoSave: false }
// 函数参数解构
function updateUser({ id, ...updates }) {
console.log(`Updating user ${id} with:`, updates)
// 模拟数据库更新
return { id, ...updates, updatedAt: new Date() }
}
const user = updateUser({
id: 123,
name: 'Bob',
email: 'bob@example.com',
age: 25
})
console.log(user)
// 过滤对象属性
function omit(obj, keys) {
const { [keys]: _, ...rest } = obj
return rest
}
function omitMultiple(obj, keys) {
const result = { ...obj }
keys.forEach(key => delete result[key])
return result
}
const data = { a: 1, b: 2, c: 3, d: 4 }
console.log(omitMultiple(data, ['b', 'd'])) // { a: 1, c: 3 }
// 条件性属性添加
function createUser(name, email, isAdmin = false) {
return {
name,
email,
createdAt: new Date(),
...(isAdmin && { role: 'admin', permissions: ['read', 'write', 'delete'] })
}
}
console.log(createUser('Alice', 'alice@example.com'))
// { name: 'Alice', email: 'alice@example.com', createdAt: Date }
console.log(createUser('Bob', 'bob@example.com', true))
// { name: 'Bob', email: 'bob@example.com', createdAt: Date, role: 'admin', permissions: [...] }
// 深度克隆(浅层)
function shallowClone(obj) {
return { ...obj }
}
// 对象数组的去重
function uniqueBy(array, key) {
const seen = new Set()
return array.filter(item => {
const value = item[key]
if (seen.has(value)) {
return false
}
seen.add(value)
return true
})
}
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice Duplicate' }
]
console.log(uniqueBy(users, 'id'))
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
}
Promise.prototype.finally()
无论 Promise 成功或失败都会执行的回调。
javascript
{
// 基本用法
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log('Data received:', data)
return data
})
.catch(error => {
console.error('Error:', error)
throw error
})
.finally(() => {
console.log('Request completed') // 无论成功失败都会执行
})
}
// 实际应用:加载状态管理
class DataLoader {
constructor() {
this.isLoading = false
this.data = null
this.error = null
}
async loadData(url) {
this.isLoading = true
this.error = null
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
this.data = await response.json()
return this.data
} catch (error) {
this.error = error
throw error
} finally {
this.isLoading = false // 无论成功失败都重置加载状态
}
}
}
// 资源清理示例
async function processFile(filename) {
const file = await openFile(filename)
try {
const data = await parseFile(file)
return await processData(data)
} catch (error) {
console.error('Processing failed:', error)
throw error
} finally {
await closeFile(file) // 确保文件始终被关闭
}
}
// 模拟网络请求的工具函数
function simulateRequest(shouldSucceed = true, delay = 1000) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldSucceed) {
resolve({ data: 'Success', timestamp: Date.now() })
} else {
reject(new Error('Request failed'))
}
}, delay)
})
}
// 使用 finally 进行日志记录
async function requestWithLogging(url) {
const startTime = Date.now()
try {
const result = await simulateRequest(Math.random() > 0.5)
console.log('Request succeeded:', result)
return result
} catch (error) {
console.error('Request failed:', error.message)
throw error
} finally {
const duration = Date.now() - startTime
console.log(`Request to ${url} completed in ${duration}ms`)
}
}
// 批量请求处理
async function batchProcess(tasks) {
const results = []
let completed = 0
for (const task of tasks) {
try {
const result = await task()
results.push({ success: true, data: result })
} catch (error) {
results.push({ success: false, error: error.message })
} finally {
completed++
console.log(`Progress: ${completed}/${tasks.length}`)
}
}
return results
}
// 示例使用
// requestWithLogging('/api/users')
}