一、**、**=指数运算符
ES2016新增指数运算符**,
2 ** 2 // 4
2 ** 4 // 16
而且多个字数运算符一起应用时,是从右边开始计算的。
2 ** 3 ** 2 //等同于2 ** (3 ** 2),2的9次方
//512
指数运算符和等号结合,可以形成一个新的运算符**=。
let a = 3;
a **= 3 //27,等价于a = a * a * a;
二、?.链判断运算符
ES2020 引入了链判断符?.,用于判断前方的属性是否有值。
若是没有值,可以赋默认值。
const firstName = message?.body?.user?.firstName || 'default';
上面代码使用了?.运算符,直接在链式调用的时候判断,左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined,从而给firstName赋上默认值'default'。
链判断运算符?.有三种写法。
- obj?.prop // 对象属性是否存在
- obj?.[expr] // 同上
- func?.(...args) // 函数或对象方法是否存在
使用注意事项:
1、短路机制
本质上,?.运算符相当于一种短路机制,只要不满足条件,就不再往下执行。
a?.[++x]
// 等同于 a == null || a == undefined ? undefined : a[++x]
上述代码中,如果a为undefined或null,那么x不会进行递增运算。也就是说,链判断运算符一旦为真,右侧的表达式就不再求值。
2、括号的影响
如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。
(a?.b).c
// 等价于 (a == null ? undefined : a.b).c
由于?.对圆括号外部没有影响,不管a对象是否存在,圆括号后面的.c总是会执行。所以一般来说,使用?.运算符的场合,不应该使用圆括号。
3、报错场合
// 构造函数
new a?.()
new a?.b()
// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`
// 链判断运算符的左侧是 super
super?.()
super?.foo
// 链运算符用于赋值运算符左侧
a?.b = c
以上全都为错误用法,会报错。
4、右侧不得为十进制数值
为了保证兼容以前的代码,允许foo?.3:0被解析成foo ? .3 : 0 (foo ? 0.3 : 0),因此规定如果?.后面紧跟一个十进制数字,那么?.不再被看成是一个完整的运算符,而是会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数(如上例为0.3)。
三、?? 空判断运算符
在之前,为属性指定默认值通常使用 || ,但使用 || 会存在=>当左侧属性值为空字符串或false或0,默认值也会生效的问题。
于是ES2020 引入了一个新的 Null 判断运算符??。它的行为类似 || ,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。如下例子,左边为null或undefined时,赋值'Hello, world!'。
const headerText = response.settings.headerText ?? 'Hello, world!';
可以与链判断运算符?.配合使用,为null或undefined的值设置默认值。
const animation = res.settings?.animation ?? 300;
使用注意事项:
??本质上是逻辑运算,它与其他两个逻辑运算符&&和||有一个优先级问题。所以如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。
// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs
// 正确写法
(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);
四、||=、&&=、??=逻辑赋值运算符
ES2021引入了三个新的逻辑赋值运算符,将逻辑运算符与赋值运算符进行结合。
// 或赋值运算符
x ||= y
// 等同于
x || (x = y)
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)
这三个运算符||=、&&=、??=相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。因为先进行逻辑运算,所以会是短路算法。
它们的一个用途是,为变量或属性设置默认值。
// 老的写法
user.id = user.id || 1;
function example(opts) {
opts.foo = opts.foo ?? 'bar';
opts.baz ?? (opts.baz = 'qux');
}
// 新的写法
user.id ||= 1;
function example(opts) {
opts.foo ??= 'bar';
opts.baz ??= 'qux';
}