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

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

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.hqwc.cn/news/115061.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【漏洞复现】广联达OA漏洞合集(信息泄露+SQL注入+文件上传)

文章目录 声明广联达OA存在信息泄露一、漏洞概述二、漏洞复现三、修复建议广联达Linkworks办公OA SQL注入漏洞后台文件上传漏洞一、产品简介二、漏洞概述三、复现环境四、修复建议 声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工…

安装Anaconda与pytorch,在IDEA中配置环境进行编程

1.官网下载与自己python版本匹配的Anaconda(注意,要想成功安装pytorch,python版本也要对应pytorch的相关版本) Anaconda官网最新版本 与自己python版本不否请查找自己版本anaconda版本对应 清华大学镜像下载 2.安装时勾选添加环境变量或者手动添加&am…

2023上半年薪资报告出炉!人均月入过万?!

最近,大家都有听到经济回暖的消息吧?经过三年口罩大考之后,2023年上半年各行各业都迎来复苏,关于职场的话题讨论也不绝于耳。 现在就业环境如何?哪些行业更有前途?大家在求职时是选择一线城市还是回二、三…

深入探究序列化与反序列化:原理、应用和最佳实践

目录 什么是对象的序列化和反序列化序列化步骤反序列化步骤案例演示Java中哪些字段不能序列化序列化与反序列化的重要性序列化与反序列化的应用场景 什么是对象的序列化和反序列化 序列化(Serialization)是指将对象转化为字节流的过程,以便于…

干货 | 中国石化化工高端新材料价格体系模型构建

以下内容整理自2023年夏季学期大数据能力提升项目《大数据实践课》同学们所做的期末答辩汇报。 随着石化行业市场日趋饱和,市场竞争日益激烈,企业利润空间不断被压缩,大多数石化企业急需转型开拓新市场,化工原料价格的波动对于石化…

4、wireshark使用教程

文章目录 一、wireshark简介二、环境三、wireshark抓包三、wireshark过滤器使用 一、wireshark简介 Wireshark是使用最广泛的一款「开源抓包软件」,常用来检测网络问题、攻击溯源、或者分析底层通信机制。 Wireshark抓包原理: 单机情况:电脑…

ESP32主板-MoonESP32

产品简介 Moon-ESP32主板,一款以双核芯片ESP32-E为主芯片的主控板,支持WiFi和蓝牙双模通信,低功耗,板载LED指示灯,引出所有IO端口,并提供多个I2C端口、SPI端口、串行端口,方便连接,…

基于PHP语言研发的抖音矩阵系统源代码开发部署技术文档分享

一、概述 本技术文档旨在介绍抖音SEO矩阵系统源代码的开发部署流程,以便开发者能够高效地开发、测试和部署基于PHP语言的开源系统。通过本文档的指引,您将能够掌握抖音SEO矩阵系统的开发环境和部署方案,从而快速地构建出稳定、可靠的短视频S…

输电线路故障诊断(Python代码,逻辑回归、决策树、随机森林、XGBoost和支持向量机五种不同方法诊断)

效果视频:输电线路故障诊断(Python代码,逻辑回归、决策树、随机森林、XGBoost和支持向量机五种不同方法诊断)_哔哩哔哩_bilibili 项目文件 code.py装载的是英文版本,图上显示英文标签及坐标,Chinese.py装载…

【Unity】ShaderGraph应用(浮动气泡)

【Unity】ShaderGraph应用(浮动气泡) 实现效果 一、实现的方法 1.使用节点介绍 Position:获取模型的顶点坐标 Simple Noise:简单的噪声,用于计算顶点抖动 Fresnel Effect:菲涅耳效应,用于实现气泡效果 计算用节点 Add&…

Pytorch(GPU)环境安装

winR:启动cmd; 输入nvidia-smi 查看cuda的配置 (1) 安装CUDA 地址:https://developer.nvidia.com/cuda-downloads 详细参考:安装CUDA与CUDNN与Pytorch(最新超级详细图文版本2023年8月最新)_pytorch安装cudnn_LyaJpunov的博客-C…

驱动开发---基于gpio子系统编写LED灯的驱动

一、GPIO子系统相关API 1.解析GPIO相关的设备树节点 struct device_node *of_find_node_by_path(const char *path) 功能:根据设备树节点路径解析设备树节点信息 参数: path:设备树所在的节点路径 /mynode0X12345678 返回值:成…