梳理 JavaScript 中空数组调用 every方法返回true 带来惊讶的问题

前言

人生总是在意外之中. 情况大概是这样的. 前两天版本上线以后, 无意中发现了一个bug, 虽然不是很大, 为了不让用户使用时感觉到问题. 还是对着一个小小的bug进行了修复, 并重新在上线一次, 虽然问题不大, 但带来的时间成本还是存在的. 以及上线后用户体验并不是很好.

问题产生的原因就在于JavaScript数组遍历方法中对于空数组返回值的问题. 空数组遍历的知识点, 在学习的过程中, 相信你肯定也接触过, 学习过. 但在使用时往往会忽略这一点.

我们以every为例


1. every 基本使用

对于every遍历方法, 这里就不过多阐述了. 主要就是遍历数组中每个元素, 执行回调函数, 当所有的元素都返回true时, 结果是true, 只要有一次遍历时, 回调函数返回false结果就是false


示例:

let arr = [40,50,60,10,20,30]// 判断数组中所有的元素是否都大于5
let bol = arr.every((item) => item > 5)
console.log('bol', bol)
// 输出结果: bol true// 判断数组中所有的元素是否都大于
let bol2 = arr.every((item) => item > 10)
console.log('bol2', bol2)
// 输出结果: bol2 false

在这个示例中, 第一次调用every 时, 会遍历所有的数组元素, 因为每一个元素都大于5, 所以回调函数会执行6次, 每次都返回true, every遍历方法最终返回true, 表示数组中每个元素都符合要求


第二次遍历时, 只会遍历4次, 因为在遍历到10 时, 回调函数返回false, 此时数组后面的元素就不需要再遍历了. 该false已经确定了最终的结果, false表示数组中包含不符号要求的元素.


every遍历方法并不需要关心具体是第几项元素不符合要求. 该方法的作用就是判断数组中是否是每一项都符合要求


2. every 空数组的问题

我们先说一下最终的结果, 空数组使用every时, 每次结果都返回true

示例:

let arr = []// 固定返回true
let bol = arr.every((item) => true)
console.log('bol', bol)
// 输出结果: bol true// 回调函数固定返回false
let bol2 = arr.every((item) => {console.log('every')return false
})
console.log('bol2', bol2);
// bol2 true

示例中, 我们对于空数组使用every遍历方法, 无论回调函数返回的是true,还是false最终的结果都是true


我们在回调函数中添加console.log("every")记录回调函数是否执行. 从运行结果来看, 会调函数并没有执行. 空数组中没有元素, 并不会执行回调函数, 也就意味这回调函数中返回的是什么值都毫无意义.

every遍历方法最终的结果true显然是一个默认值. 可以理解为调用every时, 默认返回值就是true, 然后遍历元素,执行回调函数, 只要有一次回调函数返回的false, 则作为最终结果返回false并结束遍历.


3. 规范描述

根据ECMA-262 官方描述, every方法的逻辑如下
在这里插入图片描述

这里对于最终返回值, 我将其框选出来. 通过官方描述, 最终的结果有两种情况, 其一就是默认返回true, 其二是根据回调函数执行的结果返回false,


这里我们根据表述, 自定义一个函数模拟every方法

示例

// 参数接受一个回调函数Array.prototype.myEvery = (callbackfn, thisArg) => {// 获取this, 通过数组调用该方法, this 即为数组const O = this;// 获取数组长度const len = O.length;// 确认参数callback 是一个函数. 否则报错if(typeof callbackfn !== "function"){throw new TypeError(typeof callbackfn + "is not a function")}// 遍历数组let k = 0; while(k  < len){// 转为字符串const Pk = String(key)// 判断下标是否为数组本身的属性const kPresent = O.hasOwnProperty(Pk);if(kPresent){// 获取数组元素const kValue = O[PK]// 调用回调函数, 获取回调函数的结果const testResult = Boolean( callbackfn.call(this.Arg, kValue, k, O))// 如果回调函数返回false, 则停止循环, 整体返回falseif(testResult === false){return false}}k++}// 数组元素循环完毕, 回调函数都没有返回false, 则表示数组每一项否符合要求// 最终返回truereturn true}

从代码中可以看出,every ()默认返回为 true,并且只有在回调函数执行返回 false 时才返回 false。如果数组中没有元素,那么就没有机会执行回调函数,因此方法就没有办法返回 false,只会返回默认值true


4. 场景描述

在工作中发生问题场景是这样的, 在使用vue开发, 父组件给子组件传参. 期望传入的 参数在子组件的本身数据中都包含, 则不执行后续逻辑, 如果传入的数据, 在子组件数据中有不存的项, 则更新子组件数据.

这里我将复杂业务逻辑简化为JavaScript方法的调用.

示例:

let cacheArray = [10,20,30];
function update (arr) {// 判断传入的数组每一项存在于cacheArray 中const result = arr.every((item) => cacheArray.includes(item))// 条件为true, 则表示传入数组中的数据都存在, 则不执行后续逻辑,// false, 更新cacheArray 数据, 并执行后续逻辑if(!result){cacheArray = [...arr]console.log('cacheArray', cacheArray)}
}

这个示例代码在表面上看没有任何问题, 但如果你想清空cacheArray数组. 你会发现调用update方法做不到,

当你调用update方法,并传入一个[]时, 空数组调用every的结果始终是true, 所以后面取反的结果始终为false, 根本执行更新cacheArray数组的代码.


发现问题点, 解决方案就很简单了, 修改一下判断条件, 当参数为空数组时. length必然是0, 通过判断是空数组, 根据逻辑运算符的||的短路算法规则, 不需再去判断!result

修改判断条件

if(!arr.length || !result){cacheArray = [...arr]console.log('cacheArray', cacheArray)
}

5. 总结

这就是空数组给功能带来的问题. 所以有的时候, 真的不是我们学习了所有知识, 我们就能做到万无一失. 在工作中存在很多复杂的逻辑, 一个疏忽, 或没有考虑细致都会带来问题. 不是不会, 而是所有业务逻辑交织在一起. 难免带来一些逻辑上的遗漏


对于some方法也会有相同的问题, 对于空数组会始终返回false, 这个留给你自己探讨

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

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

相关文章

JVM学习-垃圾收集器(二)

Serial回收器&#xff1a;串行回收 Serial收集器是最基本、历史最悠久的收集器JDK1.3之前新生代唯一的选择Hotpot中Client模式下的默认新生代垃圾收集器采用复制算法&#xff0c;串行回收“Stop-the-world”机制的方式执行内存回收除了年轻代之外&#xff0c;Serial收集器还提…

TG-5006CG温补晶振在WiFi6无线路由器模块的应用

WiFi6无线路由器是采用了wiFi6技术的无线网络设备&#xff0c;旨在为家庭、办公室或其他场所提供高速、稳定的无线网络连接。它不仅能实现更高的数据传输速率和更低的延迟&#xff0c;还提供了更先进的加密和安全措施&#xff0c;确保用户数据安全。为了支持这些高级功能&#…

深入 Rust 标准库,Rust标准库源代码系统分析

系列文章目录 送书第一期 《用户画像&#xff1a;平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 送书第四期 《AI时代项目经理成长之道》 …

Elasticsearch集群部署以及认证配置

文档地址&#xff1a; 官网文档地址&#xff1a; https://www.elastic.co/guide/index.html rpm包/源码下载地址&#xff1a;https://www.elastic.co/cn/downloads 源码安装-环境准备&#xff1a; node-01 192.168.95.174 node-02 192.168.95.173 node-03 …

【uniapp】CSS实现多行文本展开收起的文字环绕效果

1. 效果图 收起状态 展开状态 2. 代码实现 <view class"word-wrap" id"descriptionTxt"><view class"fold-text" v-if"isFold"><text class"fold-btn" click"changFold">全文</text&g…

使用docker完整搭建前后端分离项目

1、docker的优势&#xff0c;为啥用docker 2、docker的核心概念 镜像【Image】- 只读模板 容器【Container】- 运行镜像的一个外壳&#xff0c;相当于一个独立的虚拟机 仓库【repository】- 镜像的管理工具&#xff0c;可公开&#xff0c;可私有&#xff1b;类似git仓库 3、c…

操作教程|通过DataEase开源BI工具对接金山多维表格

前言 金山多维表格是企业数据处理分析经常会用到的一款数据表格工具&#xff0c;它能够将企业数据以统一的列格式整齐地汇总至其中。DataEase开源数据可视化分析工具可以与金山多维表格对接&#xff0c;方便企业更加快捷地以金山多维表格为数据源&#xff0c;制作出可以实时更…

包拯断案 | MySQL5.7替换路上踩过的坑 一键get解决办法@还故障一个真相

提问&#xff1a;作为DBA运维的你&#xff0c;是否有过这些烦恼 1、业务系统进行替换投产时&#xff0c;发现数据库回放并行度低 2、虽然2个数据库集群使用同一份数据&#xff0c;却在关闭双一后&#xff0c;二级从库的回放效率依旧缓慢&#xff0c;不知是什么原因&#xff1f…

机器人开源项目分享,助力一户一机器人

最初&#xff0c;因隋炀帝思念心切&#xff0c;命工匠按照柳抃的形象制作了木偶机器人&#xff0c;被认为是历史上最早的机器人之一。这些木偶机器人通过精巧设计的机关&#xff0c;能够执行坐、起、拜、伏等动作。 如今&#xff0c;随着科技的发展&#xff0c;机器人已经广泛…

从ES5迈向ES6:探索 JavaScript 新增声明命令与解构赋值的魅力

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; ES5、ES6介绍 文章目录 &#x1f4af;声明命令 let、const&#x1f35f;1 let声明符&a…

Linux磁盘初始化与fstab文件更新

环境&#xff1a; Redhat 7.9 本文操作&#xff1a; >>给disk设置分区 &#xff08;fdisk&#xff09; >>给disk设置file system格式 (mkfs ) >>创建路径&#xff0c;并将disk mount上(mkdir和mount ) >>修改fstab文件 初始化Disk 初始化前&#xff…

【计算机网络原理】对传输层TCP协议的重点知识的总结

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

桌面文件不见了怎么恢复?五种方法解决文件恢复难题,建议收藏

不小心误删除了桌面文件&#xff0c;导致文件丢失。事实上误删的文件并没有被永久删除&#xff0c;而是被移动到了回收站中&#xff0c;可以恢复这些文件。本文将分享多种方法&#xff0c;具体步骤如下。 方法一&#xff1a;从回收站中恢复 大多数操作系统都有回收站或垃圾桶的…

【C语言】结构体内存对齐:热门面试话题

&#x1f525;引言 书接上文&#xff0c;我们了解关于结构体的基本知识&#xff0c;这篇将深入剖析结构体中一个重要的知识点:内存对齐 关于内存对齐是属于热门面试话题&#xff0c;对此单独放在一篇来分享 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记…

3D工业视觉

前言 本文主要介绍3D视觉技术、工业领域的应用、市场格局等&#xff0c;主要技术包括激光三角测量、结构光、ToF、立体视觉。 一、核心内容 3D视觉技术满足工业领域更高精度、更高速度、更柔性化的需求&#xff0c;扩大工业自动化的场景。 2D视觉技术基于物体平面轮廓&#…

软件无线电学习-第二代移动通信系统过程理解

本文知识内容摘自《软件无线电原理和应用》 无线通信领域让大家感受最深的是民用移动通信的快速发展。民用移动通信在短短的二十年时间里已发展了三代&#xff1a;20世纪80年代的模拟体制(TACS/AMPS)为第一代移动通信(简称1G)&#xff1b;20世纪90年代的数字体制(GSMCDMATDMA)…

Git提交和配置命令

一、提交代码到仓库 在软件开发中&#xff0c;版本控制是一个至关重要的环节。而Git作为目前最流行的版本控制系统之一&#xff0c;为我们提供了便捷高效的代码管理和协作工具。在日常开发中&#xff0c;我们经常需要将本地代码提交到远程仓库&#xff0c;以便于团队协作和版本…

Java基础教程 - 9 集合

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 9 集合 什么是集合&…

【stm32/CubeMX、HAL库】嵌入式实验六:定时器(2)|PWM输出

参考&#xff1a; 【【正点原子】手把手教你学STM32CubeIDE开发】 https://www.bilibili.com/video/BV1Wp42127Cx/?p13&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c3989f6189fd3 《嵌入式系统基础与实践》刘黎明等编著&#xff0c;第九章定时器&#xff0c…

爱普生TG5032SFN温补晶振在机器人控制中的应用

机器人控制是机器人技术的核心组成部分&#xff0c;它涉及通过传感器采集外部环境信息&#xff0c;然后经过信号处理、运动规划和执行控制等步骤&#xff0c;最终实现机器人的运动控制和任务执行。在技术的不断更选&#xff0c;机器人控制也在不断进步和演变。智能化机器人具备…