不要再滥用可选链运算符(?.)啦!

news2025/1/22 13:14:37

可选链运算符(?.),大家都很熟悉了,直接看个例子:

const result = obj?.a?.b?.c?.d 

很简单例子,上面代码?前面的属性如果是空值(null或undefined),则result值是undefined,反之如果都不是空值,则会返回最后一个d属性值。

本文不是讲解这种语法的用法,主要是想分析下日常开发中,这种语法 滥用、乱用 的问题。

滥用、乱用

最近在code review一个公司项目代码,发现代码里用到的可选链运算符,很多滥用,用的很无脑,经常遇到这种代码:

 

const userName = data?.items?.[0]?.user?.name

↑ 不管对象以及属性有没有可能是空值,无脑加上?.就完了。

 

// react class component const name = this.state?.name // react hooks const [items, setItems] = useState([]) items?.map(...) setItems?.([]) // 真有这么写的

↑ React框架下,this.state 值不可能是空值,初始化以及set的值都是数组,都无脑加上?.

 

const item1 = obj?.item1 console.log(item1.name)

↑ 第一行代码说明obj或item1可能是空值,但第二行也明显说明不可能是空值,否则依然会抛错,第一行的?.也就没意义了。

 

if (obj?.item1?.item2) { const item2 = obj?.item1?.item2 const name = obj?.item1?.item2?.name }

↑ if 里已经判断了非空了,内部就没必要判断非空了。

问题、缺点

如果不考虑 ?. 使用的必要性,无脑滥用其实也没问题,不会影响功能,优点也很多:

  1. 不用考虑是不是非空,每个变量或属性后面加 ?. 就完了。
  2. 由于不用思考,开发效率高。
  3. 不会有空引用错误,不会有页面点点就没反应或弹错问题。

但是问题和缺点也很明显,而且也会很严重。分两点分析下:

  1. 可读性、维护性:给代码维护人员带来了很多分析代码的干扰,代码可读性和维护性都很差。
  2. 隐式过滤了异常:把异常给隐式过滤掉了,导致不能快速定位问题。
  3. 编译后代码冗余。
  4. 护眼:一串?.看着难受,特别是以一个code reviewer 角度看。

1. 可读性、维护性

可读性和维护性其实是一回事,都是指不是源代码作者的开发维护人员,在捋这块代码逻辑、修改bug等情况时,处理问题的效率,代码写的好处理就快,写的烂就处理慢,很简单道理。

 

const onClick = () => { const user = props.data?.items?.[0]?.user if (user) { // use user to do something } }

已这行代码为例,有个bug现象是点击按钮没反应,维护开发看到这块代码,就会想这一串链式属性里,是不是有可能有空值,所以导致了user是空值,没走进if里导致没反应。然后就继续分析上层组件props传输代码,看data值从哪儿传来的,看是不是哪块代码导致data或items空值了。。。

其实呢?从外部传过来的这一串属性里不会有空值的情况,导致bug问题根本不在这儿。

 

const user = props.data.items[0].user

那把?.都去掉呢?维护开发追踪问题看到这行代码,data items 这些属性肯定不能是空值,不然console就抛错了,但是bug现象里并没有抛错,所以只需要检查user能不能是空值就行了,很容易就排除了很多情况。

总结就是:给代码维护人员带来了很多分析代码的干扰,代码可读性和维护性都很差。

2. 隐式过滤了异常

 

api.get(...).then(result => { const id = result?.id // use id to do something })

比如有个需求,从后台api获取数据时,需要把结果里id属性获取到,然后进行数据处理,从业务流程上看,这个api返回的result以及id必须有值,如果没值的话后续的流程就会走不通。

然后后台逻辑由于写的有问题,导致个别情况返回的 result=null,但是由于前端这里加了?.,导致页面没有任何反应,js不抛错,console也没有log,后续流程出错了,这时候如果想找原因就会很困难,对代码熟悉还行,如果不是自己写的就只能看代码捋逻辑,如果是生产环境压缩混淆了就更难排查了。

 

api.get(...).then(result => { const id = result.id // use id to do something })

?.去掉呢?如果api返回值有问题,这里会立即抛错,后面的流程也就不能进行下去了,无论开发还是生产环境都能在console里快速定位问题,即使是压缩混淆的也能从error看出一二,或者在一些前端监控程序里也能监听到。

其实这种现象跟 try catch 里不加 throw 类似,把隐式异常错误完全给过滤掉了,比如下面例子:

 

// 这个try本意是处理api请求异常 try { const data = getSaveData() // 这段js逻辑也在try里,所以如果这个方法内部抛错了,页面上就没任何反应,很难追踪问题 const result = await api.post(url, data) // result 逻辑处理 } catch (e) { // 好点的给弹个框,打个log,甚至有的啥都不处理 }

总结就是:把异常给隐式过滤掉了,导致不能快速定位问题。

3. 编译后代码冗余

如果代码是ts,并且编译目标是ES2016,编译后代码会很长。可以看下 www.typescriptlang.org/play 效果。

image.png

Babel在个别stage下,编译效果一样。

image.png

但并不是说一点都不用,意思是尽量减少滥用,这样使用的频率会少很多,这种编译代码沉余也会少不少。

应该怎么用?

说了这么多,.? 应该怎么用呢?意思是不用吗?当然不是不能用,这个特性对于开发肯定好处很多的,但是得合理用,不能滥用。

  1. 避免盲目用,滥用,有个点儿就加问号,特别是在一个比较长的链式代码里每个属性后面都加。
  2. 只有可能是空值,而且业务逻辑中有空值的情况,就用;其它情况尽量不要用。

其实说白了就是:什么时候需要判断一个变量或属性非空,什么时候不需要。首先在使用的时候得想下,问号前面的变量或属性值,有没有可能是空值:

  1. 很明显不可能是空值,比如 React类组件里的 this.state this.props,不要用;
  2. 自己定义的变量或属性,而且没有赋值为空值情况,不要用;
  3. 某些方法或者组件里,参数和属性不允许是空值,那方法和组件里就不需要判断非空。(对于比较common的,推荐写断言,或者判断空值情况throw error)
  4. 后台api请求结果里,要求result或其内部属性必须有值,那这些值就不需要判断非空。
  5. 按正常流程走,某个数据不会有空值情况,如果是空值说明前面的流程出问题了,这种情况就不需要在逻辑里判断非空。
 

const userName = data?.items?.[0]?.user?.name // 不要滥用,如果某个属性有可能是空值,则需要?. const userName = data.items[0].user?.name // 比如data.items数组肯定不是空数组

 

const items2 = items1.filter(item => item.checked) if (items2?.length) { } // 不需要?.

 

// react class component const name = this.state?.name // 不需要?. // react hooks const [items, setItems] = useState([]) items?.map(...) // 如果setItems没有赋值空值情况,则不需要?. setItems?.([]) // 不需要?.

 

const item1 = obj?.item1 // 不需要?. console.log(item1.name)

 

const id = obj?.id // 下面代码已经说明不能是空值了,不需要?. const name = obj.name

 

if (obj?.item1?.item2) { const item2 = obj?.item1?.item2 // 不需要?. const name = obj?.item1?.item2?.name // 不需要?. }

 

const id = obj?.item?.id // 不需要?. api.get(id).then(...) // 这个api如果id是空值,则api会抛错

当然,写代码时还得多想一下属性是否可能是空值,会一定程度的影响开发效率,也一定有开发会觉得很烦,不理解,无脑写?.多容易啊,但是我从另外两个角度分析下:

  1. 我觉得一个合格的开发应该对自己的代码逻辑很熟悉,应该有责任知道哪些值可能是空值,哪些不可能是空值(并不是说所有,也有大部分了),否则就是对自己的代码了解很少,觉得代码能跑就行,代码质量自然就低。
  2. 想想在这个新特性出来之前大家是怎么写的,会对每个变量和属性都加if非空判断或者用逻辑与(&&)吗?不会吧。

总结

本文以一个 code reviewer 角度,分析了 可选链运算符(?.) 特性的滥用情况,以及“正确使用方式”,只是代表我本人的看法,欢迎大佬参与讨论,无条件接受任何反驳。

滥用的缺点:

  1. 可读性、维护性:给代码维护人员带来了很多分析代码的干扰,代码可读性和维护性都很差。
  2. 隐式过滤了异常:把异常给隐式过滤掉了,导致不能快速定位问题。
  3. 编译后代码冗余。
  4. 护眼:一串?.看着难受,特别是以一个code reviewer 角度看。

“正确用法”:

  1. 避免盲目用,滥用,有个点儿就加问号,特别是在一个比较长的链式代码里每个属性后面都加。
  2. 只有可能是空值,而且业务逻辑中有空值的情况,就用;其它情况尽量不要用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1031503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业架构LNMP学习笔记58

开始学习Tomcat: 学习目标和内容: 1)能够描述Tomcat的使用场景; 2)能够简单描述Tomcat的工作原理; 3)能够实现部署安装Tomcat; 4)能够实现和配置Tomcat的Server服务…

如何判断自己的服务器是不是中了勒索病毒,勒索病毒解密数据恢复

勒索病毒日益横行,给企业的生产经营和个人数据造成了极大的影响。但是对单一的企业和个人而言,遭受勒索病毒攻击毕竟是低频率事件,这也导致很多人中招之后,无法判断出具体的问题,进而没有及时采取应对措施,…

【谢希尔 计算机网络】第3章 数据链路层

数据链路层 数据链路层的地位 网络中的主机、路由器等都必须实现数据链路层局域网中的主机、交换机等都必须实现数据链路层不同链路层可能采用不同的数据链路层协议 数据链路层信道类型 点对点信道 使用一对一的点对点通信方式广播通信 必须使用专用的共享系电脑协议来协调这些…

【Java 基础篇】Java多线程实现文件上传详解

文件上传是Web应用程序中常见的功能之一,用户可以通过网页将文件从本地计算机上传到服务器。在处理大文件或多用户并发上传的情况下,为了提高性能和用户体验,常常使用多线程来实现文件上传功能。本文将详细介绍如何使用Java多线程实现文件上传…

【RabbitMQ实战】docker 安装RabbitMQ(bitnami)

一、搜索镜像 [rootlocalhost ~]# docker search rabbitmq NAME DESCRIPTION STARS OFFICIAL AUTOMATED rabbitmq RabbitMQ is an open source multi-pro…

携手走过四年,极智嘉(Geek+)赋能上海西门子开关智慧物流升级

日前,上海西门子开关有限公司迎来了工厂30周年庆典暨客户开放日活动,全球仓储机器人引领者极智嘉(Geek)荣幸作为优秀供应商代表受邀参会,见证其突破革新、数智飞跃的三十载辉煌历程。 客户开放日活动中,上海西门子开关有限公司开…

【SpringCloud】微服务技术栈入门2 - Nacos框架与Feign

目录 Nacos下载 Nacos 并运行配置 NacosNacos 集群Nacos 负载均衡Nacos 环境隔离Nacos 注册细节Nacos 更多配置项快速上手自动更新 Feign取代 RestTemplateFeign 自定义配置性能优化 Nacos 下载 Nacos 并运行 首先下载对应的 release 包,主要要选择已经打包编译好…

链表的回文判断

思路: 找中间节点–>逆置->比较 代码: /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head) { struct ListNode*slowhead; struct ListNode*f…

maven多模块依赖包程序包xxx不存在

背景 rpc-common 被 rpc-server、rpc-client依赖 项目地址 https://github.com/pjmike/springboot-rpc-demo mvn clean install 打包时报错 报错信息 程序包xxxx不存在 找不到符号 原因分析 原因还不清楚&#xff0c;网友们帮解答一下 解决 主pom.xml 添加 <packaging…

23个销量最高的3D扫描仪【2023】

如果你可以 3D 扫描它&#xff0c;你就可以 3D 打印它。 市场上 3D 扫描仪的种类和质量非常丰富&#xff0c;机器尺寸、功能和价格各异。 这样的选择虽然本身是一件很棒的事情&#xff0c;但也会让从无用的东西中挑选出宝石成为一件苦差事。 推荐&#xff1a;用 NSDT编辑器 快速…

如何在linux定时备份opengauss数据库(linux核心至少在GLIBC_2.34及以上)

前提环境&#xff0c;linux的核心至少在GLIBC_2.34及以上才能使用。 查看linux的glibc版本的命令如下 strings /lib64/libc.so.6 | grep GLIBC 如下图 或者用ldd --version 如下图 在官网下载对应的依赖包&#xff0c; 只需要这个lib文件即可&#xff0c;将这个包放在lin…

拓世法宝|短视频带货风潮,数字人教育书单号成销售黑马

Z世代的爸妈&#xff0c;正在搞一种很新的育儿方式。 “躺平式”带娃、“用魔法打败魔法”等新时代育儿方式频频登上热搜&#xff0c;作为与互联网共同成长起来的一代&#xff0c;Z世代父母们更热衷于通过网络攻略获得和分享知识和经验&#xff0c;更注重个性的养育方式&#…

ChatGPT所引用的参考文献根本不存在?如何正确使用AI工具?

从ChatGPT推出以来&#xff0c;一直都是一个热点话题&#xff0c;尤其是在高等教育领域&#xff0c;大家展开了非常激烈的讨论。 巴斯大学对ChatGPT进行测试时发现&#xff0c;ChatGPT生成的论文&#xff0c;其参考文献的引用格式看起来很完美&#xff0c;有作者姓名和期刊名称…

文件、预处理、位运算

10.2 数据文件概述 10.2.1 ASCII文件与二进制文件 ASCII文件就是“将需要保存到文件的信息使用ASCII字符表示&#xff0c;然后按照顺序将每个字符的ASCII码存储到文件中”。ASCII文件的优点是编码方式公开&#xff0c;可以被其它的文本编辑器打开&#xff1b;其缺点是效率比较…

第一章 计算机网络基础

目录 1.1 网络体系结构 1.1.1 OSI/RM七层参考模型 1.1.2 OSI/RM和TCP/IP模型的比较 1.1.3 五层协议的体系结构 1.1.4 计算机1向计算机2发送数据过程 1.1.5 TCP/IP体系结构的具体实现 1.2 网络设备概述 1.2.1 互联设备与OSI的对应关系 1.2.2 集线器(HUB) 1.2.3 网桥(B…

新势力在智能化路上,正抢了Tier 1的生意

作者 | Amy 编辑 | 德新 上半年的汽车行业价格内卷&#xff0c;下半年则一下资本涌入&#xff0c;风起云涌。 先是蔚来拿到了11亿美元来自中东的投资&#xff0c;紧接着7月大众以7亿美元投资小鹏汽车&#xff0c;8月哪吒完成70亿元Crossover轮投资。传闻中&#xff0c;还有大众…

GE IS220PAICH2A 336A4940CSP11 控制脉冲模块

GE IS220PAICH2A 336A4940CSP11控制脉冲模块是一种用于工业自动化和控制系统的模块&#xff0c;通常用于监测和生成脉冲信号&#xff0c;以控制各种设备和过程。以下是可能与该控制脉冲模块相关的一些产品功能&#xff1a; 脉冲生成&#xff1a;GE IS220PAICH2A 336A4940CSP11控…

JCEF中js与java交互、js与java相互调用

jcef中js与java相互调用&#xff0c;java与js相互调用&#xff0c;chrome与java相互调用&#xff0c;java与chrome相互调用、jcef与java相互调用 前提&#xff1a;https://blog.csdn.net/weixin_44480167/article/details/133170970&#xff08;java内嵌浏览器CEF-JAVA、jcef、…

天玑科技PBData信创数据库私有云平台荣获“专题展优秀成果奖”

9月15-16日&#xff0c;由湖南省人民政府、工业和信息化部联合主办的2023世界计算大会在湖南长沙盛大启幕。天玑科技作为云服务、信息技术应用创新领域优秀的解决方案提供商受邀参会参展。 大会以“计算万物 湘约未来——计算产业新变革”为主题&#xff0c;全国政协副主席、民…

GE IS420UCSCH2A-C-V0.1-A模拟量输入模块

GE IS420UCSCH2A-C-V0.1-A 模拟量输入模块是一种用于数据采集和监测的电子模块&#xff0c;通常应用于工业控制系统、监测设备和自动化系统中。以下是可能与该模拟量输入模块相关的一些产品特点&#xff1a; 多通道输入&#xff1a; GE IS420UCSCH2A-C-V0.1-A 模拟量输入模块通…