踩坑经验:JavaScript 中的 for...of 和 for...in 循环

在 JavaScript 编程中,for...offor...in 是常用的循环语法,但它们在使用时可能会引发一些意想不到的问题。本文将分享我在使用这两种循环时所遇到的坑和经验。

两者的区别:

  1. 适用对象类型
    • for…of:主要用于遍历可迭代对象(例如数组、字符串、Set、Map等),可以获取到迭代对象的值。
    • for…in:主要用于遍历对象的属性(包括原型链上的属性),可以获取到属性名(键)。
  1. 遍历顺序
    • for…of:按照对象的顺序迭代,一般用于遍历有序集合。
    • for…in:无法保证属性的遍历顺序,可能会导致属性的无序输出。
  1. 迭代内容
    • for…of:迭代的是对象的值本身,例如数组中的元素、字符串中的字符等。
    • for…in:迭代的是对象的属性名,需要通过属性名访问属性值。
  1. 支持情况
    • for…of:在 ES6 中引入,适用于可迭代对象,如数组、字符串等。
    • for…in:在早期版本的 JavaScript 中就存在,用于遍历对象的属性。但是不适用于数组等可迭代对象,因为它会遍历出额外的属性。
  1. 性能
    • for…of:通常性能比 for…in 更好,因为它不需要遍历原型链上的属性。

示例代码演示两者的不同用法:

// for...of 遍历数组
const arr = [1, 2, 3, 4];
for (const element of arr) {console.log(element); // 输出数组的每个元素
}// for...in 遍历对象的属性
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {console.log(key);      // 输出属性名 a, b, cconsole.log(obj[key]); // 输出属性值 1, 2, 3
}// for...of 遍历数组
const arr = [1, 2, 3, 4];
for (const element of arr) {console.log(element); // 输出数组的每个元素
}// for...in 遍历对象的属性
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {console.log(key);      // 输出属性名 a, b, cconsole.log(obj[key]); // 输出属性值 1, 2, 3
}

总之,如果你想遍历数组或其他可迭代对象的值,使用 for…of;如果你想遍历对象的属性,使用 for…in

for…of 遍历数组的陷阱:

当使用 for...of 循环来遍历数组时,我们通常是为了遍历数组的元素,而不是索引。然而,在使用 for...of 循环时,有一些常见陷阱需要避免,特别是关于循环索引和遍历顺序的问题。下面是如何正确使用 for...of 循环来遍历数组,以及如何避免这些陷阱的解释:

1. 遍历元素而非索引: 使用 for...of 循环时,我们直接遍历数组的元素,而不需要关心索引的细节。这样可以使代码更加简洁易读。例如:


const array = [1, 2, 3, 4, 5];
for (const element of array) {console.log(element); // 输出数组的每个元素
}

2. 避免使用索引: 避免在 for...of 循环中使用额外的索引变量,因为 for...of 循环本身已经直接提供了数组的每个元素。这有助于减少代码复杂性和错误的机会。不推荐的写法:


const array = [1, 2, 3, 4, 5];
for (let i = 0; i < array.length; i++) {console.log(array[i]); // 避免这种额外使用索引的方式
}

3. 保持遍历顺序: for...of 循环保证按照数组中的顺序进行遍历,因此它适用于需要按顺序处理元素的场景。这确保了元素的处理顺序与它们在数组中的位置一致。

4. 不会遍历稀疏元素: for...of 循环不会遍历数组中的稀疏元素(未赋值的元素),只会遍历有实际值的元素。这有助于避免不必要的处理。

5. 适用于可迭代对象: 除了数组,for...of 循环还适用于其他可迭代对象,如字符串、Set、Map 等。这使得代码具有更广泛的适用性。

综上所述,使用 for...of 循环来遍历数组是一种更直观、简洁的方式,可以避免许多在传统 for 循环中容易犯的错误。通过专注于元素而非索引,保持遍历顺序,并充分利用循环的简洁性,我们可以提高代码的可读性和可维护性,减少错误的风险。

for…in 遍历对象的不可靠性:

在使用 for...in 循环时,可能会遇到一些问题,其中包括遍历顺序的不确定性和遍历到原型属性的风险。下面是对这些问题的探讨:

1. 遍历顺序的不确定性: for...in 循环无法保证遍历对象属性的顺序。这是因为对象属性在 ECMAScript 规范中被定义为无序的。因此,使用 for...in 循环来依赖属性遍历的特定顺序是不可靠的。

2. 遍历到原型属性的风险: for...in 循环会遍历对象自身属性以及继承自原型链的属性。这可能会导致意外的属性遍历,尤其是当我们只想遍历对象自身的属性时。

3. 原型属性被遍历:


function Person() {this.name = 'Alice';
}Person.prototype.age = 30;const person = new Person();for (const prop in person) {console.log(prop); // 输出 'name' 和 'age'
}

/*** @param {Function} fn* @return {Array}*/
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
fn = function (n) {return String(n > 5);
}Array.prototype.groupBy = function (fn) {const myMap = new Map();const keys = Object.keys(this);console.log(keys);for (const key in keys) {if (myMap.has(fn(this[key]))) {myMap.set(fn(this[key]), myMap.get(fn(this[key])).concat([this[key]]));} else {myMap.set(fn(this[key]), [this[key]]);}}const myObj = {};for (const [key, value] of myMap) {myObj[key] = value;}return myObj;
};array.groupBy(fn);
/*** [1,2,3].groupBy(String) // {"1":[1],"2":[2],"3":[3]}*/

image.png

image.png
4. 使用 hasOwnProperty 过滤原型属性:


for (const prop in person) {if (person.hasOwnProperty(prop)) {console.log(prop); // 仅输出 'name'}
}

5. 遍历可枚举属性:


Object.defineProperty(person, 'country', {value: 'USA',enumerable: true
});for (const prop in person) {console.log(prop); // 输出 'name'、'age' 和 'country'
}

6. 遍历不可枚举属性:


Object.defineProperty(person, 'address', {value: '123 Main St',enumerable: false
});for (const prop in person) {console.log(prop); // 仅输出 'name' 和 'age'
}

7. 遍历顺序问题:


const obj = { a: 1, b: 2, c: 3 };for (const prop in obj) {console.log(prop); // 输出 'a'、'b' 和 'c',但顺序不确定
}

8. 遍历字符串属性:


const str = 'Hello';for (const char in str) {console.log(char); // 输出 '0'、'1'、'2'、'3' 和 '4'
}

综上所述,尽管 for...in 循环在某些情况下可以派上用场,但要特别小心遍历顺序的不确定性和遍历到原型属性的风险。在需要遍历对象属性时,推荐使用 Object.keysObject.valuesObject.entries 等方法,以获得更可靠的遍历结果。或者,考虑使用 for...of 循环来遍历数组和可迭代对象,以避免这些问题。

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

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

相关文章

数据结构-带头双向循环链表的实现

前言 带头双向循环链表是一种重要的数据结构&#xff0c;它的结构是很完美的&#xff0c;它弥补了单链表的许多不足&#xff0c;让我们一起来了解一下它是如何实现的吧&#xff01; 1.节点的结构 它的节点中存储着数据和两个指针&#xff0c;一个指针_prev用来记录前一个节点…

Git详解及使用

Git简介 Git 是一种分布式版本控制系统&#xff0c;它可以不受网络连接的限制&#xff0c;加上其它众多优点&#xff0c;目前已经成为程序开发人员做项目版本管理时的首选&#xff0c;非开发人员也可以用 Git 来做自己的文档版本管理工具。 大概是大二的时候开始接触和使用Gi…

云计算——ACA学习 云计算核心技术

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 写在前面 本系列将会持续更新云计算阿里云ACA的学习&#xff0c;了解云计算及网络安全相关…

DeepSpeed加速大模型训练

DeepSpeed是微软推出的一个框架&#xff0c;可以对Pytorch的模型进行包装&#xff0c;提供了加快模型的训练速度&#xff0c;降低对GPU显存的占用&#xff0c;以及方便进行分布式训练等等高级特性。在这里我也对DeepSpeed进行了测试&#xff0c;看看是否能提高我的transformer模…

Maven 插件 maven-antrun-plugin 执行 ant 脚本

Ant 相信大家都不陌生&#xff0c;你可以把它理解为使用 xml 格式描述的一系列命令处理工具。它是一种基于Java的build工具。理论上来说&#xff0c;它有些类似于&#xff08;Unix&#xff09;C中的make、有些类似于基于shell命令编写的sh脚本文件。Ant 用 Java 的类来扩展。&a…

多维时序 | MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测

多维时序 | MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测 目录 多维时序 | MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测基本介绍模型特点程序设计参考资料 基本介绍 本次运行测试环境MATLAB2021b&#xff0c;MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测。代码说明&#xff1a…

Java mail邮件开发 OA办公系统

目录 1 Java mail邮件开发 OA办公系统 1.1 //用户登录 1.2 //用户注册 1.3 //根据邮箱账号查询用户ID Java mail邮件开发 OA办公系统package com.email.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement;<

POSTGRESQL 关于2023-08-14 数据库自动启动文章中使用KILL 来进行配置RELOAD的问题解释...

开头还是介绍一下群&#xff0c;如果感兴趣Polardb ,mongodb ,MySQL ,Postgresql ,redis &#xff0c;SQL SERVER ,ORACLE,Oceanbase 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请加 liuaustin3微信号 &…

Oracle执行计划

Oracle执行计划 1. 什么是执行计划Oracle explain使用3. Explain执行顺序 1. 什么是执行计划 执行计划是一条查询语句在Oracle中的执行过程或访问路径的描述。 执行计划描述了SQL引擎为执行SQL语句进行的操作&#xff1b;分析SQL语句相关的性能问题或仅仅质疑查询优化器的决定…

蔚来李斌卖手机:安卓系统,苹果售价,一年一发

‍作者 | Amy 编辑 | 德新 车圈大佬的玩法真让人寻不着套路&#xff01; 苹果的库克和小米的雷布斯&#xff0c;甚至是FF贾老板准备许久&#xff0c;都想分一块新能源车的蛋糕&#xff0c;蔚来李斌却反手进军手机界&#xff0c;从宣布造手机到手机入网仅仅隔了一年。 近期…

说几个常见的语法糖

目录 面试回答 知识扩展 如何解语法糖&#xff1f; 糖块一、swith 支持 String 与枚举 糖块二、泛型 糖块三、自动装箱与拆箱 糖块四、枚举 糖块五、条件编译 糖块六、断言 糖块七、数值字面量 糖块八、for-each 糖块九、try-with-resource 可能遇到的坑 泛型 自…

Beats:安装及配置 Metricbeat (一)- 8.x

在我之前的文章&#xff1a; Beats&#xff1a;Beats 入门教程 &#xff08;一&#xff09;Beats&#xff1a;Beats 入门教程 &#xff08;二&#xff09; 我详细描述了如何在 Elastic Stack 7.x 安装及配置 Beats。在那里的安装&#xff0c;它通常不带有安全及 Elasticsearc…

MapReduce介绍

目录 ​一、什么是MapReduce 二、MapReduce 的设计思想 2.1 分而治之 2.2 构建抽象模型&#xff1a;Map和Reduce 2.3 隐藏系统层细节 三、MapReduce 的框架原理 3.1 MRv1工作原理 3.1.1 MRv1架构工作原理图 3.1.1.1 流程说明 3.1.1.1.1 作业的提交 3.1.1.1.2 作业的初始化 3…

【AI大模型】训练Al大模型 (上篇)

大模型超越AI 前言 洁洁的个人主页 我就问你有没有发挥&#xff01; 知行合一&#xff0c;志存高远。 目前所指的大模型&#xff0c;是“大规模深度学习模型”的简称&#xff0c;指具有大量参数和复杂结构的机器学习模型&#xff0c;可以处理大规模的数据和复杂的问题&#x…

【Java】Queue中增加删除方法的区别

offer&#xff0c;add 区别&#xff1a; 一些队列有大小限制&#xff0c;因此如果想在一个满的队列中加入一个新项&#xff0c;多出的项就会被拒绝。 这时新的 offer 方法就可以起作用了。它不是对调用 add() 方法抛出一个 unchecked 异常&#xff0c;而只是得到由 offer() 返…

题目:售货员的难题(状压dp)

售货员的难题 题目描述输入输出格式输入格式&#xff1a;输出格式&#xff1a; 输入输出样例输入样例#1&#xff1a;输出样例#1&#xff1a; 思路AC代码&#xff1a; 题目描述 某乡有n个村庄( 1 < n < 16 )&#xff0c;有一个售货员&#xff0c;他要到各个村庄去售货&am…

consul限制注册的ip

假设当前服务器的ip是&#xff1a;192.168.56.130 1、允许 所有ip 注册(验证可行) consul agent -server -ui -bootstrap-expect1 -data-dir/usr/local/consul -nodedevmaster -advertise192.168.56.130 -bind0.0.0.0 -client0.0.0.0 2、只允许 当前ip 注册 consul agent -…

Leetcode33 搜索旋转排序数组

题解&#xff1a; /*** 旋转排序数组可分为N1 N2两个部分&#xff0c;如&#xff1a;[4,5,6,7,1,2,3]&#xff0c;N1为[4,5,6,7]&#xff0c;N2为[1,2,3]** 必然满足以下两个条件&#xff1a;* 1. N1和N2都是分别递增的&#xff1b;* 2. N1中的所有元素大于N2中的所有元素;** …

【Python机器学习】实验12 基于神经网络的回归-分类实验

文章目录 神经网络的回归例1 基于神经网络的回归(简单例子)1.1 导入包1.2 构造数据集&#xff08;随机构造的&#xff09;1.3 构造训练集和测试集1.4 构建神经网络模型1.5 采用训练数据来训练神经网络模型 实验1 基于神经网络的分类(鸢尾花数据集)1.1 导入包1.2 构造数据集1.3 …

Selenium浏览器自动化测试框架简单介绍

selenium简介 介绍   Selenium [1] 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样。支持的浏览器包括IE&#xff08;7, 8, 9, 10, 11&#xff09;&#xff0c;Mozilla Firefox&#xff0c;Safari&#xff0c;Googl…