【08】ES6:运算符的扩展

一、指数运算符

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

x ** y

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

指数运算符是右结合的。

a ** b ** c 等于 a ** (b ** c)

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

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

let a = 1.5
a **= 2 // 等同于 a = a * a

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

二、链判断运算符

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

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

// 错误的写法
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) // 函数或对象方法是否存在
// 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)短路机制

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

a?.[++x]
// 等同于
a == null ? 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,因此规定如果 ?. 后面紧跟一个十进制数字,那么 ?. 不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。

三、Null 判断运算符

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

语法

leftExpr ?? rightExpr
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 时,才会返回右侧的值。

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 的值设置默认值。

const animationDuration = response.settings?.animationDuration ?? 300

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

注意点

(1)短路机制

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

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)优先级问题

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

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,新的写法比老的写法更紧凑一些。

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

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

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

function example(opts) {
   
	opts.foo = opts.foo ?? 'bar'
	opts.baz ?? (opts.baz = 'qux')
}

function example(opts) {
   
	opts.foo ??= 'bar'
	opts.baz ??= 'qux'
}

相关推荐

  1. 08ES6运算符扩展

    2023-12-14 06:28:04       39 阅读
  2. ES6运算符扩展

    2023-12-14 06:28:04       17 阅读
  3. ES6---扩展运算符详解

    2023-12-14 06:28:04       33 阅读
  4. 07ES6:对象扩展

    2023-12-14 06:28:04       26 阅读
  5. js - es6 - 扩展运算符用法

    2023-12-14 06:28:04       18 阅读
  6. ES6扩展

    2023-12-14 06:28:04       17 阅读
  7. ES6之函数新增扩展

    2023-12-14 06:28:04       37 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-14 06:28:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-14 06:28:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-14 06:28:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-14 06:28:04       20 阅读

热门阅读

  1. 【07】ES6:对象的扩展

    2023-12-14 06:28:04       26 阅读
  2. NLP知识点 - perplexity 困惑度

    2023-12-14 06:28:04       30 阅读
  3. 【centos】【python】程序单例

    2023-12-14 06:28:04       37 阅读
  4. 通过Jenkins将应用发布到K8s1.24.3

    2023-12-14 06:28:04       30 阅读
  5. 微信小程序 轮播图且跳转微信公众号文章

    2023-12-14 06:28:04       34 阅读
  6. 前端传的true后端变false?

    2023-12-14 06:28:04       39 阅读
  7. 3D三维数据格式分类(种类/软件平台)

    2023-12-14 06:28:04       42 阅读
  8. react中使用craco自定义配置

    2023-12-14 06:28:04       38 阅读
  9. 前端打包工具之Webpack5

    2023-12-14 06:28:04       37 阅读
  10. FAQ:Inheritance篇 — virtual functions

    2023-12-14 06:28:04       37 阅读
  11. 数据分析用哪个系统

    2023-12-14 06:28:04       36 阅读
  12. lua脚本的基本语法,以及Redis中简单使用

    2023-12-14 06:28:04       45 阅读