【08】ES6:运算符的扩展

一、指数运算符

指数运算符(**)返回第一个操作数取第二个操作数的幂的结果。

javascript 复制代码
x ** y

2 ** 2 // 4
2 ** 3 // 8

指数运算符是右结合的。

javascript 复制代码
a ** b ** c 等于 a ** (b ** c)

2 ** 3 ** 2  // 相当于 2 ** (3 ** 2)  512

指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。

javascript 复制代码
let a = 1.5
a **= 2 // 等同于 a = a * a

let b = 4
b **= 3 // 等同于 b = b * b * b;

二、链判断运算符

链判断运算符(可选链运算符)?. 允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。

?. 运算符的功能类似于 . 链式运算符,不同之处在于,在引用为空的情况下不会引起错误,而是返回 undefined。(在链式调用的时候判断,左侧的对象是否为 null 或 undefined,如果是的,就不再往下运算,而是返回 undefined。)

javascript 复制代码
// 错误的写法
const  firstName = message.body.user.firstName || 'default'

// && 运算符
const firstName = (message
	&& message.body
	&& message.body.user
	&& message.body.user.firstName) || 'default'

// ?: 三元运算符
const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined

// .? 可选链运算符
const firstName = message?.body?.user?.firstName || 'default'
const fooValue = myForm.querySelector('input[name=foo]')?.value

链判断运算符 ?. 有三种写法

  • obj?.prop // 对象属性是否存在
  • obj?.[expr] // 同上
  • func?.(...args) // 函数或对象方法是否存在
javascript 复制代码
// obj?.[expr] 用法:没有发现匹配返回 null,发现匹配返回一个数组
let hex = '#C0FFEE'.match(/#([A-Z]+)/i)?.[1]

a?.b
// 等同于
a == null ? undefined : a.b

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于(如果 a?.b() 里面的 a.b 有值,但不是函数,不可调用,那么 a?.b() 是会报错的。)
a == null ? undefined : a.b()

a?.()
// 等同于(如果 a 不是 null 或 undefined ,但也不是函数,那么 a?.() 会报错。)
a == null ? undefined : a()

注意点

(1)短路机制

本质上,?. 运算符相当于一种短路机制,只要不满足条件,就不再往下执行。

javascript 复制代码
a?.[++x]
// 等同于
a == null ? undefined : a[++x]

上面代码中,如果 a 是 undefined 或 null ,那么 x 不会进行递增运算。也就是说,链判断运算符一旦为真,右侧的表达式就不再求值。

(2)括号的影响

如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。

javascript 复制代码
(a?.b).c
// 等价于
(a == null ? undefined : a.b).c

上面代码中,?. 对圆括号外部没有影响,不管 a 对象是否存在,圆括号后面的 .c 总是会执行。

一般来说,使用 ?. 运算符的场合,不应该使用圆括号。

(3)报错场合

以下写法是禁止的,会报错。

javascript 复制代码
// 构造函数
new a?.()
new a?.b()

// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`

// 链判断运算符的左侧是 super
super?.()
super?.foo

// 链运算符用于赋值运算符左侧
a?.b = c

(4)右侧不得为十进制数值

为了保证兼容以前的代码,允许 foo?.3:0 被解析成 foo ? .3 : 0,因此规定如果 ?. 后面紧跟一个十进制数字,那么 ?. 不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。

三、Null 判断运算符

Null 判断运算符 ??,又称空值合并运算符。是一个逻辑运算符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。

语法

javascript 复制代码
leftExpr ?? rightExpr
javascript 复制代码
const nullValue = null
const emptyText = '' // 空字符串,是一个假值,Boolean('') === false
const someNumber = 42

const valA = nullValue ?? 'valA 的默认值'
// valA 的默认值
const valB = emptyText ?? 'valB 的默认值'
// ''(空字符串虽然是假值,但不是 null 或者 undefined)
const valC = someNumber ?? 0
// 42

常见用途

(1)为变量赋默认值

读取对象属性的时候,如果某个属性的值是 null 或 undefined,有时候需要为它们指定默认值。常见做法是通过 || 运算符指定默认值。

逻辑或运算符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,'' 或 0 或 false)时。

使用空值合并运算符 ??可以避免这种情况,只有运算符左侧的值为 null 或 undefined 时,才会返回右侧的值。

javascript 复制代码
let myText = '' // An empty string (which is also a falsy value)

let notFalsyText = myText || 'Hello world'
console.log(notFalsyText) // Hello world

let preservingFalsy = myText ?? 'Hi neighborhood'
console.log(preservingFalsy); // '' (as myText is neither undefined nor null)

(2)与链判断运算符 ?. 配合使用为 null 或 undefined 的值设置默认值。

javascript 复制代码
const animationDuration = response.settings?.animationDuration ?? 300

上面代码中,如果 response.settings 是 null 或 undefined ,或者 response.settings.animationDuration 是 null 或 undefined ,就会返回默认值 300。也就是说,这一行代码包括了两级属性的判断。

注意点

(1)短路机制

ORAND 逻辑运算符相似,当左表达式不为 null 或 undefined 时,不会对右表达式进行求值。

javascript 复制代码
function A() {
	console.log('函数 A 被调用了')
	return undefined
}
function B() {
	console.log('函数 B 被调用了')
	return false
}
function C() {
  console.log('函数 C 被调用了')
  return 'foo'
}

console.log(A() ?? C())
// 依次打印 '函数 A 被调用了'、'函数 C 被调用了'、'foo'
// A() 返回了 undefined,所以运算符两边的表达式都被执行了

console.log(B() ?? C())
// 依次打印 '函数 B 被调用了'、'false'
// B() 返回了 false(既不是 null 也不是 undefined)
// 所以右侧表达式没有被执行

(2)优先级问题

?? 运算符 和其他逻辑运算符 &&|| 之间的运算优先级/运算顺序是未定义的,组合使用时,必须用括号表明优先级,否则会报错。

javascript 复制代码
null || undefined ?? 'foo' // 抛出 SyntaxError
true || undefined ?? 'foo' // 抛出 SyntaxError

(null || undefined) ?? 'foo' // 返回 'foo'

四、逻辑赋值运算符

ES2021 引入了三个新的逻辑赋值运算符(logical assignment operators),将逻辑运算符与赋值运算符进行结合。

逻辑赋值运算符 说明
||= 逻辑或赋值运算符(x ||= y)运算仅在 x 为假值时为其赋值。等同于 x || (x = y)。
&&= 逻辑与赋值运算符(x &&= y)运算仅在 x 为真值时为其赋值。等同于 x && (x = y)。
??= 逻辑空赋值运算符(x ??= y)仅在 x 是空值(null 或 undefined)时对其赋值。等同于 x ?? (x = y)。

这三个运算符 ||=&&=??= 相当于先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。

用途:为变量或属性设置默认值。

user.id 属性如果不存在,则设为 1,新的写法比老的写法更紧凑一些。

javascript 复制代码
// 老的写法
user.id = user.id || 1

// 新的写法
user.id ||= 1

参数对象 opts 如果不存在属性 foo 和属性 baz,则为这两个属性设置默认值。

javascript 复制代码
function example(opts) {
	opts.foo = opts.foo ?? 'bar'
	opts.baz ?? (opts.baz = 'qux')
}

function example(opts) {
	opts.foo ??= 'bar'
	opts.baz ??= 'qux'
}
相关推荐
王解29 分钟前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁34 分钟前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂38 分钟前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_6 小时前
【Linux】多线程(概念,控制)
linux·运维·前端
蜗牛快跑2136 小时前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy6 小时前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js