ECMAScript 2021 新特性
1 String.prototype.replaceAll()
在JavaScript中,replace()方法只替换字符串中的第一个实例。如果我们想替换字符串中的所有匹配项,唯一的办法就使用全局正则表达式。
新特性replaceAll()会返回一个新的字符串,字符串中所有的匹配项都会被替换掉。模式可以是字符串或正则表达式,替换的内容可以是字符串或是为每个匹配项执行的函数。
原本的replace()只能替换掉第一个匹配项:
1 2 3
| const str = "审批 审批 审批 审批 " const newStr = str.replace("审批", "人事") console.log(newStr)
|
如果想要完全匹配替换需要写全局正则表达式:
1 2 3
| const str = "审批 审批 审批 审批" const newStr = str.replace(/审批/g, "人事") console.log(newStr)
|
新的replaceAll()特性:
1 2 3
| const str = "审批 审批 审批 审批" const newStr = str.replaceAll("审批", "人事") console.log(newStr)
|
2 Promise.any
ES2020已经通过了Promise的allSettled()方法。ES2021 Promise阵营将有一个新的成员,any()。
当Promise列表中的任意一个promise成功resolve则会短路并返回第一个resolve的结果状态, 如果所有的promise均reject,则抛出异常AggregateError,表示所有请求失败。
Promise.any()与Promise.race()十分容易混淆,务必注意区分。Promise.race() 一旦某个promise触发了resolve或者reject,就直接返回了该状态的结果。
即使Promise在resolve的之前被reject,Promise.any()仍将返回第一个resolve的结果:
1 2 3 4 5 6 7
| Promise.any([ new Promise((resolve, reject) => setTimeout(reject('workflow'), 100)), new Promise((resolve, reject) => setTimeout(resolve('staff'), 1000)), new Promise((resolve, reject) => setTimeout(resolve('holiday'), 2000)) ]) .then((value) => console.log(`结果:${value}`)) .catch((err) => console.log(err));
|
当所有promise都为reject会抛出异常AggregateError: All promises were rejected:
1 2 3 4 5 6 7
| Promise.any([ Promise.reject('Error one'), Promise.reject('Error two'), Promise.reject('Error three') ]) .then((value) => console.log(`结果:${value}`)) .catch((err) => console.log(err));
|
3 逻辑运算符和赋值表达式
在JavaScript中,有很多赋值操作符和逻辑操作符,在新的草案下,我们可以组合逻辑运算符和赋值运算符。
a &&= b 当a值存在时,将b变量赋值给a:
a ||= b 当a值不存在时,将b变量赋值给a:
a ??= b 当a值为null或者undefined时,将b变量赋值给a:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| let a let b = 10 a ??= b console.log(a)
a = false a ??= b console.log(a)
const navigations = [ { title: '工作台', path: '/' }, { title: '审批', path: '/workflow' }, { path: '/setting' } ];
for (const navigation of navigations) { page.title ??= '默认'; }
console.table(pages);
|
4 Private Methods & Private Accessors
4.1 私有方法 Private Methods
私有方法只能在定义它的类内部访问,专用方法名称以#开头。由于setType()是私有方法,所以personObj.setType返回undefined,用undefined作函数会引发TypeError。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Person { #setType() { console.log('Private'); } show() { this.#setType(); } }
const personObj = new Person(); personObj.show(); personObj.setType();
|
4.2 私有访问者 Private Accessors
可以通过在函数名称前添加#,使得访问器函数私有。在下面的代码中,name是公共的,可以像普通属性一样读取它, 而age则是私有。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Person { get name() { return 'able'; } set name(value) {} get #age() { return 18; } set #age(value) {} }
const personObj = new Person(); console.log(personObj.name); console.log(personObj.age);
|
5 WeakRefs
当我们通过const, let, var创建一个变量时,垃圾收集器GC将永远不会从内存中删除该变量,只要它的引用仍然存在且可访问。而WeakRef对象包含针对对象的弱引用,针对对象的弱引用是不会阻止垃圾收集器GC的回收的,则GC可以在任何时候删除它。
例如有如下场景:
跟踪某个对象调用某一特定方法的次数,超过1000条则做对应提示。
如果我们使用Map,虽然可以实现需求,但是会发生内存溢出,因为传递给doSomething()的每个对象都永久保存在map中,并且不会被GC回收。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let map = new Map();
function doSomething(obj) { }
function useObject(obj) { doSomething(obj); let called = map.get(obj) || 0; called++; if (called > 1000) { console.log('调用次数已超过1000次'); } map.set(obj, called); }
|
所以,可以通过WeakMap()或者WeakSet()来使用WeakRefs。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let wmap = new WeakMap();
function doSomething(obj) { }
function useObject(obj) { doSomething(obj); let called = wmap.get(obj) || 0; called++; if (called > 1000) { console.log('调用次数已超过1000次'); } wmap.set(obj, called); }
|
因为是弱引用,所以WeakMap、WeakSet的键值对是不可枚举的。WeakSet和WeakMap相似,但是每个对象在WeakSet中只可能出现一次,WeakSet中所有对象都是唯一的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let ws = new WeakSet(); let foo = {}; let bar = {};
ws.add(foo); ws.add(bar);
ws.has(foo); ws.has(bar);
ws.delete(foo);
ws.has(foo); ws.has(bar);
|
WeakSet与Set相比有以下两个区别:
WeakSet只能是对象集合,而不能是任何类型的任意值
WeakSet弱引用,集合中对象引用为弱引用,如果没有其他对WeakSet对象的引用,则会被GC回收
最后,WeakRef实例有一个方法deref(),返回引用的原始对象,如果原始对象被回收,则返回undefined。下面其在斐波那契数列计算中缓存的妙用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const cache = new Map();
const setValue = (key, obj) => { cache.set(key, new WeakRef(obj)); };
const getValue = (key) => { const ref = cache.get(key); if (ref) { return ref.deref(); } };
const fibonacciCached = (number) => { const cached = getValue(number); if (cached) return cached; const sum = calculateFibonacci(number); setValue(number, sum); return sum; };
|