ECMAScript 2021(ES12)
Logical Assignment Operators
&&= (逻辑与赋值)
只有当左侧操作数为真值时,才会进行赋值操作。
javascript
{
let num1 = 5
let num2 = 10
num1 &&= num2
console.log(num1) // 10
let num3 = 0
num3 &&= 99
console.log(num3) // 0 (因为 0 是假值,不会赋值)
// 等价于
// num1 && (num1 = num2)
// if (num1) {
// num1 = num2
// }
}
||= (逻辑或赋值)
只有当左侧操作数为假值时,才会进行赋值操作。
javascript
{
let num1
let num2 = 10
num1 ||= num2
console.log(num1) // 10
let num3 = 'existing'
num3 ||= 'default'
console.log(num3) // 'existing' (已有值,不会被替换)
// 等价于
// num1 || (num1 = num2)
// if (!num1) {
// num1 = num2
// }
// 实际应用:设置默认值
let config = {}
config.timeout ||= 5000
config.retries ||= 3
console.log(config) // { timeout: 5000, retries: 3 }
}
??= (空值合并赋值)
只有当左侧操作数为 null
或 undefined
时,才会进行赋值操作。
javascript
{
let num1
let num2 = 10
num1 ??= num2
console.log(num1) // 10
// 与 ||= 的区别
let num3 = false
num3 ??= num2
console.log(num3) // false (false 不是 null 或 undefined,不会赋值)
let num4 = null
num4 ??= 123
console.log(num4) // 123
// 等价于
// num1 ?? (num1 = num2)
// 实际应用:保留 0、false、'' 等假值
let settings = {
volume: 0,
enabled: false,
theme: null
}
settings.volume ??= 50 // 保持 0
settings.enabled ??= true // 保持 false
settings.theme ??= 'dark' // 设置为 'dark'
console.log(settings) // { volume: 0, enabled: false, theme: 'dark' }
}
Numeric Separators
使用下划线 _
作为数字分隔符,提高大数字的可读性。
javascript
{
// 整数
const million = 1_000_000
const billion = 1_000_000_000
console.log(million) // 1000000
console.log(billion) // 1000000000
// 小数
const decimal = 1000.12_34_56
console.log(decimal) // 1000.123456
// 二进制、八进制、十六进制
const binary = 0b1010_0001_1000_0101
const octal = 0o777_444
const hex = 0xFF_EC_DE_5E
console.log(binary) // 41349
console.log(octal) // 261924
console.log(hex) // 4293837150
// 科学计数法
const scientific = 1.234_567e8
console.log(scientific) // 123456700
// 注意:分隔符不影响实际值,只是视觉上的分组
console.log(1_000 === 1000) // true
}
Promise.any()
返回第一个成功(fulfilled)的 Promise,所有 Promise 都失败时才会拒绝。
javascript
{
// 基本用法:返回最快成功的 Promise
Promise.any([
new Promise(resolve => setTimeout(() => resolve('Fast'), 100)),
new Promise(resolve => setTimeout(() => resolve('Slow'), 500)),
Promise.reject('Error')
])
.then(result => console.log(result)) // 'Fast'
// 与 Promise.race() 的区别
Promise.race([
Promise.reject('Error 1'),
new Promise(resolve => setTimeout(() => resolve('Success'), 100))
])
.catch(err => console.log('Race failed:', err)) // 'Race failed: Error 1'
Promise.any([
Promise.reject('Error 1'),
new Promise(resolve => setTimeout(() => resolve('Success'), 100))
])
.then(result => console.log('Any succeeded:', result)) // 'Any succeeded: Success'
// 所有 Promise 都失败时返回 AggregateError
Promise.any([
Promise.reject('Error 1'),
Promise.reject('Error 2'),
Promise.reject('Error 3')
])
.catch(err => {
console.log(err.name) // 'AggregateError'
console.log(err.errors) // ['Error 1', 'Error 2', 'Error 3']
})
// 实际应用:多个资源竞争加载
async function loadResource() {
try {
const data = await Promise.any([
fetch('/api/data-server1'),
fetch('/api/data-server2'),
fetch('/api/data-server3')
])
return await data.json()
} catch (error) {
console.log('All servers failed:', error.errors)
throw new Error('All servers are unavailable')
}
}
}
String.prototype.replaceAll()
替换字符串中所有匹配的模式,无需使用全局正则表达式。
javascript
{
const str = 'I belong to ES12 new API, ES12 is great, ES12!'
// 传统方法:只替换第一个匹配
console.log(str.replace('ES12', 'ES2021'))
// 'I belong to ES2021 new API, ES12 is great, ES12!'
// 传统方法:使用全局正则替换所有
console.log(str.replace(/ES12/g, 'ES2021'))
// 'I belong to ES2021 new API, ES2021 is great, ES2021!'
// 新方法:replaceAll 直接替换所有
console.log(str.replaceAll('ES12', 'ES2021'))
// 'I belong to ES2021 new API, ES2021 is great, ES2021!'
// 使用函数作为替换值
const result = 'hello world hello'.replaceAll('hello', (match, offset) => {
return `${match}(${offset})`
})
console.log(result) // 'hello(0) world hello(12)'
// 使用正则表达式(必须带全局标志)
console.log(str.replaceAll(/ES12/g, 'ES2021'))
// 'I belong to ES2021 new API, ES2021 is great, ES2021!'
// 错误用法:正则表达式不带全局标志会报错
// console.log(str.replaceAll(/ES12/, 'ES2021'))
// TypeError: String.prototype.replaceAll called with a non-global RegExp argument
// 实际应用:安全地替换特殊字符
const template = 'Hello {{name}}, welcome to {{place}}!'
const filled = template
.replaceAll('{{name}}', 'Alice')
.replaceAll('{{place}}', 'Wonderland')
console.log(filled) // 'Hello Alice, welcome to Wonderland!'
}
WeakRef
创建对对象的弱引用,不会阻止垃圾回收。
注意: 正确使用 WeakRef 需要仔细考虑,建议尽量避免使用。垃圾回收的时机和行为在不同 JavaScript 引擎中可能不同。
javascript
{
// 基本用法
const obj = { name: 'example', data: [1, 2, 3] }
const weakRef = new WeakRef(obj)
// 获取引用的对象
const target = weakRef.deref()
if (target) {
console.log(target.name) // 'example'
} else {
console.log('Object has been garbage collected')
}
// 实际应用:缓存管理
class Cache {
constructor() {
this.cache = new Map()
}
set(key, value) {
this.cache.set(key, new WeakRef(value))
}
get(key) {
const ref = this.cache.get(key)
if (!ref) return undefined
const value = ref.deref()
if (value === undefined) {
// 对象已被回收,清理缓存
this.cache.delete(key)
return undefined
}
return value
}
cleanup() {
for (const [key, ref] of this.cache.entries()) {
if (ref.deref() === undefined) {
this.cache.delete(key)
}
}
}
}
const cache = new Cache()
let largeObject = { data: new Array(1000000).fill('data') }
cache.set('large', largeObject)
console.log(cache.get('large')) // 对象存在
largeObject = null // 移除强引用
// 在垃圾回收后,cache.get('large') 可能返回 undefined
}
FinalizationRegistry
当对象被垃圾回收时执行清理回调。
javascript
{
// 创建清理注册表
const registry = new FinalizationRegistry((heldValue) => {
console.log(`Cleaning up: ${heldValue}`)
})
// 注册对象
let obj = { name: 'test' }
registry.register(obj, 'test-object')
// 当 obj 被垃圾回收时,会执行回调
// 实际应用:DOM 元素计数器示例
class Counter {
constructor(element) {
this.ref = new WeakRef(element)
this.count = 0
// 注册清理
const registry = new FinalizationRegistry(() => {
console.log('Counter element was garbage collected')
this.stop()
})
registry.register(element, undefined, this)
this.start()
}
start() {
if (this.timer) return
const tick = () => {
const element = this.ref.deref()
if (element) {
element.textContent = ++this.count
} else {
console.log('Element no longer exists')
this.stop()
}
}
tick()
this.timer = setInterval(tick, 1000)
}
stop() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
}
}
// 使用示例(在 DOM 环境中)
/*
const counterElement = document.createElement('div')
document.body.appendChild(counterElement)
const counter = new Counter(counterElement)
// 5秒后移除元素
setTimeout(() => {
counterElement.remove()
}, 5000)
*/
}