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

news2024/9/20 21:27:00

前言

人生总是在意外之中. 情况大概是这样的. 前两天版本上线以后, 无意中发现了一个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, 则停止循环, 整体返回false
          if(testResult === false){
            return false
          }
        }

        k++
      }

      // 数组元素循环完毕, 回调函数都没有返回false, 则表示数组每一项否符合要求
      // 最终返回true
      return 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.coloradmin.cn/o/1685958.html

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

相关文章

ArrayList与LinkedList

内存 内存缓存 预先将数据写到容器等数据存储单元中&#xff0c;就是软件内存缓存。 内存缓存淘汰机制 FIFO&#xff08;First in ,First Out&#xff09;&#xff08;先进先出&#xff09; LFU (Least Frequently Used) (频繁的最后淘汰) LRU(Least Recently Used) &#…

Lumines推出RGBL彩色混合LED

Luminus Devices倾心打造了一款崭新的4合1 RGBL&#xff08;红绿蓝绿石灰&#xff09;LED系列&#xff0c;专为舞台与建筑照明领域量身打造&#xff0c;满足对高显色指数&#xff08;CRI&#xff09;与高输出颜色混合的苛刻需求。这一创新之举&#xff0c;无疑是照明技术的一次…

K8S认证|CKA题库+答案| 7. 调度 pod 到指定节点

7、调度 pod 到指定节点 您必须在以下Clusterd/Node上完成此考题&#xff1a; Cluster Master node Worker node hk8s master …

DFA 算法

为什么要学习这个算法 前一段时间遇到了瓶颈&#xff0c;因为词库太多了导致会有一些速度过慢&#xff0c;而且一个正则表达式已经放不下了&#xff0c;需要进行拆分正则才可以。 正好我以前看过有关 dfa 的介绍&#xff0c;但是并没有深入的进行研究&#xff0c;所以就趁着周…

ubuntu22.04下 easyconnect+输入法安装

先使用对应ubuntu版本的easyconnect安装 sudo dpkg -i EasyConnect_x64_7_6_7_3.deb 下载压缩包servicePack&#xff0c;并解压缩 cd 下载路径/servicePack sudo cp * /usr/share/sangfor/EasyConnect/ 打开easyConnect /usr/share/sangfor/EasyConnect/EasyConnect 此处…

docker 安装 SonarQube

文章目录 docker 安装 SonarQube一、修改句柄二、创建挂载文件夹三、拉取镜像四、修改 PG 库4.1、创建用户4.2、创建库 五、启动和挂载六、访问七、安装插件 docker 安装 SonarQube 版本&#xff1a;8.9 对 JDK 8 最大支持为 8.9 版本 一、修改句柄 #修改文件句柄数量&#…

投骰子——(随机游戏的控制)

精华点在于&#xff1a;利用封装&#xff0c;函数之间的良好调用&#xff0c;从而清晰明了的解决问题。 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> # include<stdlib.h> # include<time.h> # include"math.h" # define ARR_LEN 10 # d…

YoloV9改进策略:注意力改进|HCANet全局与局部的注意力模块CAFM|二次创新|即插即用

摘要 本文是在CAFM的基础上做了一些修改&#xff0c;算是二次创新吧&#xff01;修改后的模块对比原来的模型有了很大的提升。我用改进后的模型来改进YoloV9&#xff0c;精度得到了大幅度的提升&#xff0c;即插即用&#xff0c;简单易懂。为了方便大家写论文&#xff0c;我现…

十年磨一剑“2024成都电子信息展会”推动电子产业全球发展

2024成都电子展&#xff0c;招商工作已接近尾声&#xff0c;这场盛大的展会不仅是电子信息行业的一次盛会&#xff0c;更是中国西部电子信息产业发展的重要里程碑。自2013年起&#xff0c;中国&#xff08;西部&#xff09;电子信息博览会便选择成都作为其永久的举办地&#xf…

pod容器基础概念

一 Pod基础概念&#xff1a; ①Pod是kubernetes中最小的资源管理组件&#xff0c;Pod也是最小化运行容器化应用的资源对象。一个 Pod代表着集群中运行的一个进程。 ②kubernetes中其他大多数组件都是围绕着Pod来进行支撑和扩展Pod功能的&#xff0c; 例如&#xff0c;用于管…

2024年5月22日19:57:32第一部分

家庭财务管理系统 1.逻辑上相邻 2.单链表适应动态变化 1.定义函数 2.定义结构体 sturcrt{stu[100]; phoen[100]; }stu; 3.完善主函数

笔记-iOS消息转发机制和使用

消息转发机制的回顾以及涉及的几个方法 一、OC消息发送原理 消息转发机制 1、由于OC的动态特性&#xff0c;只有当程序运行起来之后&#xff0c;才知道要真正执行哪个函数&#xff08;动态绑定&#xff09;。在编译过程向类发送了其无法理解的消息并不会报错&#xff0c;因为…

基于双向长短期记忆BiLSTM对消费者投诉进行多类分类

前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记…

FTP如何端口映射?

FTP&#xff08;File Transfer Protocol&#xff09;是一种用于在网络上进行文件传输的协议。在FTP协议中&#xff0c;客户端和服务器通过不同的端口进行通信&#xff0c;其中控制连接使用端口号21&#xff0c;数据连接使用端口号20。由于网络环境的限制&#xff0c;一些情况下…

协变(List泛型作为方法参数时的父类子类问题)

有段时间没搞.net的项目了&#xff08;没办法&#xff0c;谁让国内JAVA流行是事实&#xff09;。最近又回归.net&#xff08;哪里需要哪里搬~&#xff09;。 接收到需求后&#xff0c;一顿输出&#xff0c;结果…咦?编译失败??? 错误信息&#xff1a; CS1503:参数1:无法…

Django介绍:探索Python最受欢迎的Web框架

文章目录 Django是什么Django的核心特性1. MTV架构2. 自带的Admin后台管理系统3. ORM&#xff08;对象关系映射&#xff09;4. 强大的表单处理5. 完善的文档和活跃的社区 快速入门&#xff1a;使用Django创建一个简单的Web应用步骤1&#xff1a;安装Django步骤2&#xff1a;创建…

lambdastream

lambda 匿名函数 为了简化java的匿名内部类 事件监听ActionListener 接口&#xff08;外部类&#xff09; 内部类 类在其他地方用不到&#xff0c;索性就把这个类定义在类的内部使用 好处&#xff1a;1.内部可以使用外部类成员 2.其他地方发现不了 往往内部类只用一次&…

电力巡检穿戴式智能手环:让巡检不孤立无援

电力巡检穿戴式智能手环:让巡检不孤立无援 在电力巡检的广袤天地里中&#xff0c;电力工作人员他们身着工装&#xff0c;头戴安全帽&#xff0c;手持仪器&#xff0c;穿梭在高压线路与铁塔之间。他们的健康状态&#xff0c;直接关系到电力作业的安全与效率。如今&#xff0c;电…

【投稿资讯】区块链会议CCF A -- SP 2025 截止6.6、11.14 附录用率

会议名称&#xff1a;46th IEEE Symposium on Security and Privacy( S&P&#xff09; CCF等级&#xff1a;CCF A类学术会议 类别&#xff1a;网络与信息安全 录用率&#xff1a;2023年 195/1147&#xff0c;2024年录用了17篇和区块链相关的论文 Topics of interest inc…

JWT的详解

一.什么是JWT JWT&#xff08;JSON Web Token&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在网络应用间安全地传递信息。它是一种紧凑的、自包含的方式&#xff0c;用于在用户和服务之间以 JSON 对象的形式安全地传输信息。 JWT 主要由三部分…