六、ES2020(ES11)
19. 空值合并运算符(Nullish coalescing Operator)
空值合并操作符( ?? )是一个逻辑操作符,当左侧的操作数为 null或者undefined时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或操作符(||)不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如'',0,NaN,false)时。
const name = "" ?? "Barry" const name2 = "" || "Barry" console.log(name); // "" console.log(name2); // "Barry" const age = 0 ?? 18; const age2 = 0 || 18; console.log(age); // 0 console.log(age2); // 18
20. 可选链 Optional chaining
20.1 语法
可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为 null 或者 undefined 的情况下不会引起错误,该表达式短路返回值是 undefined。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined。
当尝试访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能确定哪些属性必定存在,可选链操作符也是很有帮助的。
const user = { name: "Barry", age: 18, address: { street: '成华大道', getNum () { return '188号' } } }
在之前的语法中,想获取到深层属性或方法,不得不做前置校验,否则很容易命中 Uncaught TypeError: Cannot read property...
这种错误,这极有可能让你整个应用挂掉。
const street = user && user.address && user.address.street const num = user && user.address && user.address.getNum && user.address.getNum() console.log(street, num)
使用可选链
const street2 = user?.address?.street const num2 = user?.address?.getNum?.() console.log(street2, num2)
20.2 注意点
可选链不能用于赋值
let object = {}; object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment
21. BigInt
BigInt
是一种内置对象,它提供了一种方法来表示大于 2的53次方 \- 1
的整数。这原本是 Javascript 中可以用 Number
表示的最大数字。BigInt
可以表示任意大的整数。
21.1 使用方式一:数字后面增加 n
const bigInt = 9007199254740993n console.log(bigInt) console.log(typeof bigInt) // bigint // `BigInt` 和 [`Number`]不是严格相等的,但是宽松相等的。 console.log(1n == 1) // true console.log(1n === 1) // false // `Number` 和 `BigInt` 可以进行比较。 1n < 2 // ↪ true 2n > 1 // ↪ true
21.2 使用方式二:使用 BigInt 函数
const bigIntNum = BigInt(9007199254740993n) console.log(bigIntNum)
21.3 运算
let number = BigInt(2); let a = number + 2n; // 4n let b = number * 10n; // 20n let c = number - 10n; // -8n console.log(a); console.log(b); console.log(c);
21.4 注意点
BigInt 不能用于 [ Math
] 对象中的方法;不能和任何 [ Number
] 实例混合运算,两者必须转换成同一种类型。在两种类型来回转换时要小心,因为 BigInt
变量在转换成 [ Number
] 变量时可能会丢失精度。
22. String.prototype.matchAll()
matchAll()
方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。
const regexp = /t(e)(st(\d?))/g; const str = "test1test2" const arr = [...str.matchAll(regexp)] console.log(arr);
23. Promise.allSettled()
我们都知道 Promise.all() 具有并发执行异步任务的能力。但它的最大问题就是如果其中某个任务出现异常(reject),所有任务都会挂掉,Promise直接进入reject 状态。
场景:现在页面上有三个请求,分别请求不同的数据,如果一个接口服务异常,整个都是失败的,都无法渲染出数据
我们需要一种机制,如果并发任务中,无论一个任务正常或者异常,都会返回对应的的状态,这就是 Promise.allSettled 的作用
const promise1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('promise1') }, 3000) }) } const promise2 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('promise2') }, 1000) }) } const promise3 = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject('error promise3') }, 2000) }) } // Promise.all 会走到catch里面 Promise.all([promise1(), promise2(), promise3()]).then(res => { console.log('all-res', res); }).catch(err => { console.log('all-err', err); }) // Promise.allSettled 不管有没有错误,三个的状态都会返回 Promise.allSettled([promise1(), promise2(), promise3()]).then(res => { console.log('allSettled-res', res); }).catch(err => { console.log('allSettled-err', err); })
七、ES2021(ES12)
25. 逻辑运算符和赋值表达式(&&=,||=,??=)
25.1 &&=
逻辑与赋值 x &&= y
等效于:
x && (x = y);
上面的意思是,当x为真时,x=y。具体请看下面的示例:
let a = 1; let b = 0; a &&= 2; b &&= 2; console.log(a); // 2 console.log(b); // 0
25.2 ||=
逻辑或赋值(x ||= y
)运算仅在 x
为 false 时赋值。x ||= y
等同于:x || (x = y);
const a = { duration: 50, title: "" } a.duration ||= 20; console.log(a.duration); // 50 a.title ||= "Barry" console.log(a);
25.3 ??=
逻辑空赋值运算符 ( x ??= y
) 仅在 x
是 nullish[3] ( null
或 undefined
) 时对其赋值。
x ??= y
等价于:x ?? (x = y);
const a = { duration: 50 }; a.duration ??= 10; console.log(a.duration); // 50 a.speed ??= 25; console.log(a.speed); // 25
function config(options) { options.duration ??= 100; options.speed ??= 25; return options; } config({ duration: 125 }); // { duration: 125, speed: 25 } config({}); // { duration: 100, speed: 25 }
26. String.prototype.replaceAll()
replaceAll() 方法返回一个新字符串,新字符串中所有满足 pattern 的部分都会被replacement 替换。pattern 可以是一个字符串或一个 RegExp,replacement可以是一个字符串或一个在每次匹配被调用的函数。
原始字符串保持不变。
const str = 'aabbccdd'; const newStr = str.replaceAll('b', '*') console.log(newStr); // 'aa**bbccdd'
使用正则表达式搜索值时,它必须是全局的。
'aabbcc'.replaceAll(/b/, '.'); TypeError: replaceAll must be called with a global RegExp.
27. 数字分隔符
欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。比如,1000
可以写作1,000
。
ES2021
中允许 JavaScript 的数值使用下划线(_
)作为分隔符。
let budget = 1_000_000_000_000; budget === 10 ** 12 // true
这个数值分隔符没有指定间隔的位数,也就是说,可以每三位添加一个分隔符,也可以每一位、每两位、每四位添加一个。
123_00 === 12_300 // true 12345_00 === 123_4500 // true 12345_00 === 1_234_500 // true
小数和科学计数法也可以使用数值分隔符。
// 小数 0.000_001 // 科学计数法 1e10_000
数值分隔符有几个使用注意点。
- 不能放在数值的最前面(leading)或最后面(trailing)。
- 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
- 科学计数法里面,表示指数的
e
或E
前后不能有分隔符。
下面的写法都会报错。
// 全部报错 3_.141 3._141 1_e12 1e_12 123__456 _1464301 1464301_
28. Promise.any
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。
const promise1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('promise1') }, 3000); }) } const promise2 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('promise2') }, 2000); }) } const promise3 = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject('promise3 error') }, 1000); }) } Promise.any([promise1(), promise2(), promise3(),]).then(res => { // 只要有一个请求成功 就会返回第一个请求成功的 console.log('res', res); // 会返回promise2 }).catch(err => { console.log('err', err); })
只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态。
Promise.any() 跟 Promise.race() 方法很像,只有一点不同,就是 Promise.any() 不会因为某个 Promise 变成 rejected 状态而结束,必须等到所有参数 Promise 变成 rejected 状态才会结束。