ES2020让代码更优美的运算符 (?.) (??)


Posted in Javascript onJanuary 04, 2021

链判断运算符(?.)

非常好用、常用,搭配Null 判断运算符使用,效果更佳,完美!

来,上代码:

我们通常获取一个对象多层的属性值时,需要进行多次的判断。如不判断,一个为空则报错,导致后面无法继续下去。

// error
const firstName = message.body.user.firstName;

// ok
const firstName = (message
 && message.body
 && message.body.user
 && message.body.user.firstName) || 'default';

也可以用三元运算符?:来判断单个值,下面例子中,必须先判断fooInput是否存在,才能读取fooInput.value。

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

一气呵成,直接在链式调用的时候判断,左侧的对象是否为null或undefined。如果是,就不再往下运算,直接返回undefined

链判断运算符有三种用法。

  • obj?.prop // 对象属性
  • obj?.[expr] // 同上
  • func?.(...args) // 函数或对象方法的调用
"#C0FFEE".match(/#([A-Z]+)/i)?.[1] // 无匹配返回null,匹配则返回一个数组

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

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

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

a?.()
// 等同于
a == null ? undefined : a()

上面代码中,特别注意后两种形式,如果a?.b()里面的a.b不是函数,那么a?.b()是会报错的。a?.()也是如此,如果a不是null或undefined,但也不是函数,那么a?.()会报错。

使用这个运算符,有几个注意点。

(1)短路机制

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

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

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

(2)delete 运算符

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

上面代码中,如果a是undefined或null,会直接返回undefined,而不会进行delete运算。

(3)括号的影响

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

(a?.b).c
// 等价于
(a == null ? undefined : a.b).c

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

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

(4)报错场合

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

// 构造函数
new a?.()
new a?.b()

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

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

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

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

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

Null 判断运算符(??)

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

const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;

上面的三行代码都通过||运算符指定默认值,属性的值如果为''或false或0,默认值也会生效。

为了避免这种情况,ES2020 引入了一个新的 Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;

上面代码中,默认值只有在左侧属性值为null或undefined时,才会生效。

这个运算符的一个目的,就是跟链判断运算符?.配合使用,为null或undefined的值设置默认值。

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

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

这个运算符很适合判断函数参数是否赋值:

function Component(props) {
 const enable = props?.enable ?? true;
 // …
}

// 上面代码等同于
function Component(props) {
 const {
  enable: enable = true,
 } = props;
 // …
}

??有一个运算优先级问题,它与&&和||的优先级孰高孰低。现在的规则是,如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。

// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs

上面四个表达式都会报错,必须加入表明优先级的括号。

(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);

(lhs ?? middle) && rhs;
lhs ?? (middle && rhs);

(lhs || middle) ?? rhs;
lhs || (middle ?? rhs);

(lhs ?? middle) || rhs;
lhs ?? (middle || rhs);

ES版本更新的福利

在几个月前的腾讯开发者大会上,有位热心市民向ES开发者请教了这个问题,刚好借这篇文章的话题,在这里分享给大家!

关于ES版本更新,为什么ES5到ES6是大幅度书籍式的更新,此后版本更新只新增稀少的几个新技术?

原因:“怕你们学不动!”

其实,前者的更新方式,要学那么多东西,那么厚的一本天书,会给人一种望而却步的感觉,还没进门就先跪了。

后者来说,只是几个新技术,可能半天一天的,就能完全掌握并大结局了,收获的成就感会更多更快!

到此这篇关于ES2020让代码更优美的运算符 (?.) (??)的文章就介绍到这了,更多相关ES2020 运算符 (?.) (??)内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
JQuery 写的个性导航菜单
Dec 24 Javascript
js固定DIV高度,超出部分自动添加滚动条的简单方法
Jul 10 Javascript
javascript 密码框防止用户粘贴和复制的实现代码
Feb 17 Javascript
Javascript基础教程之switch语句
Jan 18 Javascript
使用Jquery实现每日签到功能
Apr 03 Javascript
javascript实现平滑无缝滚动
Aug 09 Javascript
javascript之Array 数组对象详解
Jun 07 Javascript
JavaScript遍历求解数独问题的主要思路小结
Jun 12 Javascript
Bootstrap导航条学习使用(一)
Feb 08 Javascript
使用 Vue 绑定单个或多个 Class 名的实例代码
Jan 08 Javascript
微信小程序收货地址API兼容低版本解决方法
May 18 Javascript
实现一个 Vue 吸顶锚点组件方法
Jul 10 Javascript
详解阿里Node.js技术文档之process模块学习指南
Jan 04 #Javascript
手写Vue源码之数据劫持示例详解
Jan 04 #Vue.js
js定时器出现第一次延迟的原因及解决方法
Jan 04 #Javascript
JavaScript实现页面高亮操作提示和蒙板
Jan 04 #Javascript
js正则表达式简单校验方法
Jan 03 #Javascript
vue+vant 上传图片需要注意的地方
Jan 03 #Vue.js
vue调用微信JSDK 扫一扫,相册等需要注意的事项
Jan 03 #Vue.js
You might like
php将数据库中的电话号码读取出来并生成图片
2008/08/31 PHP
php中用socket模拟http中post或者get提交数据的示例代码
2013/08/08 PHP
php的declare控制符和ticks教程(附示例)
2014/03/21 PHP
PHP实现显示照片exif信息的方法
2014/07/11 PHP
Symfony2创建页面实例详解
2016/03/18 PHP
用javascript将数据库中的TEXT类型数据动态赋值到TEXTAREA中
2007/04/20 Javascript
nullJavascript中创建对象的五种方法实例
2013/05/07 Javascript
使用jquery自定义鼠标样式满足个性需求
2013/11/05 Javascript
控制input输入框中提示信息的显示和隐藏的方法
2014/02/12 Javascript
Jquery实现仿腾讯娱乐频道焦点图(幻灯片)特效
2015/03/06 Javascript
javascript实现控制div颜色
2015/07/07 Javascript
JS+CSS实现简单的二级下拉导航菜单效果
2015/09/21 Javascript
学习Angularjs分页指令
2016/07/01 Javascript
JavaScript随机打乱数组顺序之随机洗牌算法
2016/08/02 Javascript
AngularJS自定义插件实现网站用户引导功能示例
2016/11/07 Javascript
前端axios下载excel文件(二进制)的处理方法
2018/07/31 Javascript
[09:33]2015国际邀请赛第四日TOP10
2015/08/08 DOTA
python 根据正则表达式提取指定的内容实例详解
2016/12/04 Python
Python2.7下安装Scrapy框架步骤教程
2017/12/22 Python
详解Tensorflow数据读取有三种方式(next_batch)
2018/02/01 Python
python使用正则表达式来获取文件名的前缀方法
2018/10/21 Python
实例详解python函数的对象、函数嵌套、名称空间和作用域
2019/05/31 Python
Python和Anaconda和Pycharm安装教程图文详解
2020/02/04 Python
python可视化text()函数使用详解
2020/02/11 Python
Python 通过监听端口实现唯一脚本运行方式
2020/05/05 Python
Python爬虫爬取微信朋友圈
2020/08/06 Python
Python开发入门——迭代的基本使用
2020/09/03 Python
HTML5 CSS3新的WEB标准和浏览器支持
2009/07/16 HTML / CSS
Room Mate Hotels美国:西班牙酒店品牌
2018/04/10 全球购物
屈臣氏官方旗舰店:亚洲享负盛名的保健及美妆零售商
2019/03/15 全球购物
优秀演讲稿范文
2013/12/29 职场文书
消防宣传口号
2014/06/16 职场文书
学校安全教育月活动总结
2014/07/07 职场文书
导游词怎么写
2015/02/04 职场文书
幼儿园语言教学反思
2016/02/23 职场文书
2019年入党思想汇报格式与要求
2019/06/25 职场文书