哪些 for in 和 for of 以及遍历迭代踩过的坑

前言

起因是这样的,面试官先问我讲讲Set 这种数据结构,我框框讲完后面试官又问有哪些方法可以遍历它,我脑海中有forEach 方法可以遍历,但是我知道面试官最想听到的肯定不是这个答案,因为这也太小儿科了,但是问题来了,我本人也忘了Set 这种数据结构有没有 迭代器(Iterator) 这种属性,到底是用 for in 还是用 for of 遍历,面完了后突然想到迭代器和Set都是es6新出的内容,怎么可能会没有,看来真的是简单问题里面看细节,输的很彻底,还是需要对自我不断拷打......

1. Set

Set 是一种集合数据结构,用于存储唯一值,即每个值在 Set 中只出现一次,常用于数组去重操作:

可以看到 Set 中的值是唯一的,不允许重复值,给数组去重的 Set 对象的每个值其实也像对象一样是一种键值对的形式,因为在 Set 中值本身就是唯一的键。与普通对象(Object)不同,Set 中的值没有对应的键名,只有值本身。

除此之外,Set 常见方法还有add、has、size、delete、cleaer等:

javascript 复制代码
// 创建一个空的 Set 实例
const mySet = new Set();

// 向 Set 中添加值
mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(2); // 试图添加重复的值,但由于 Set 中值是唯一的,所以这个操作不会改变 Set 的内容

// 判断 Set 中是否包含某个值
console.log(mySet.has(2)); // 输出: true
console.log(mySet.has(4)); // 输出: false

// 返回 Set 中值的数量
console.log(mySet.size); // 输出: 3

// 从 Set 中删除一个值
mySet.delete(2);

// 再次判断 Set 中是否包含某个值
console.log(mySet.has(2)); // 输出: false

// 清空 Set 中的所有值
mySet.clear();
console.log(mySet.size); // 输出: 0

使用 add 方法向其中添加值,使用 has 方法判断是否包含某个值,使用 size 属性获取值的数量,使用 delete 方法删除值,使用 clear 方法清空 Set 中的所有值。

2. for in

for...in 是一种用于遍历对象的属性的语句。它可以遍历对象自身的可枚举属性 以及继承的可枚举属性(来自原型链) 。一般情况下,for...in 语句用于遍历普通对象的属性,而不是数组或类似数组的对象。

for...in 遍历普通对象

javascript 复制代码
const obj = {a: 1, b: 2, c: 3}; 
for (let key in obj) { 
    console.log(key); // 输出 a b c,是获取对象自身的可枚举属性进行遍历
}

for...in 遍历数组

javascript 复制代码
const obj = [1,2,3]; 
for (let key in obj) { 
    console.log(key); // 输出 0 1 2
}

注意

在第一个例子中由于 for...in 会遍历对象的原型链 ,因此在使用时需要注意可能会遍历到继承的属性,或者通过 hasOwnProperty 方法进行判断。

在第二个例子中for...in 循环遍历了数组 obj 的索引,因为数组也是一种特殊的对象,其索引 会被视为属性名 。所以,for...in 循环输出的是数组的索引(即属性名),而不是数组元素的值。

3. for of

当需要遍历一个集合(如数组、字符串、Map、Set 等)的元素时,可以使用 for...of 循环。for...of 可以简洁而直观的方式来遍历集合中的每个元素,无需关心索引等底层细节。 一个对象如果要具备可被for...of循环调用就必须在Symbol.iterator属性上有可调用的 Iterator 接口

使用 for...of 遍历普通对象

使用 for...of 遍历数组

第一个例子可以看到普通对象是没有可迭代属性Symbol.iterator所以会报错。

第二个例子中,for...of 循环只能用于可迭代对象 (具有 Symbol.iterator 属性的对象),数组是具有迭代器属性可以被循环迭代,所以对于普通对象,仍然需要使用 for...in 循环或其他方式来进行遍历。

4. 迭代器

官网对于迭代器是这么介绍的:

也就是说,一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。for...of循环内部调用的是数据结构的Symbol.iterator方法。

原生具备 Iterator 接口的数据结构如下。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

关于迭代器更详细内容可以去es6官网查看:es6 中的 Iterator 和 for...of 循环

5. 回归正题:哪些方法可以遍历Set?

  1. 使用forEach方法
javascript 复制代码
const mySet = new Set([1, 2, 3, 4, 5]);

mySet.forEach(element => {
    console.log(element);
}); // 输出1 2 3 4 5
  1. 使用for...of循环
javascript 复制代码
const mySet = new Set([1, 2, 3, 4, 5]);

for (const element of mySet) {
    console.log(element);
} // 输出1 2 3 4 5
  1. 转换为数组后遍历
javascript 复制代码
const mySet = new Set([1, 2, 3, 4, 5]);
const myArray = Array.from(mySet);
// for of 遍历
for (const element of myArray) {
    console.log(element);
} 

//  map 遍历
const mySet = new Set([1, 2, 3, 4, 5]);
const myArray = Array.from(mySet).map(element => { console.log(element)});

自我总结

别再傻傻地分不清for...infor...of 啦,for...ofSetMap都是es6新出的,需要具有迭代器属性才能用for...of进行遍历,此外StringArray也是具有迭代属性可以被for...of进行遍历,普通对象没有迭代属性会报错.

for...in可以专门用来遍历对象,它可以遍历可枚举属性,去得到对象的键,而数组没有对象所谓的键所以会默认用下标来当成键遍历, 遍历数组会输出下标值。

分不清可以这样想,我们平时写项目的时候遍历一个对象不都是 v-for:"item in data" 吗,这就是for...in的遍历,剩下的数组呀,Set、Map 啦不就是给剩下的for...of遍历啦。

最后关于迭代器这个属性,字节前几年也专门考过这一题,感兴趣可以去看看这篇:这是来自字节俩道面试题。。。汗颜

相关推荐
yunxi_05几秒前
分布式文件服务实战稿:从本地存储到对象存储的架构升级
后端·面试
IT从业者张某某17 分钟前
less 工具 OpenHarmony PC适配实践
前端·microsoft·less
Chan1631 分钟前
【 Java八股文面试 | Redis篇 缓存问题、持久化、分布式锁 】
java·数据库·redis·后端·spring·缓存·面试
行走的陀螺仪1 小时前
vue3-封装权限按钮组件和自定义指令
前端·vue3·js·自定义指令·权限按钮
isyuah1 小时前
vite-plugin-openapi-ts CLI 使用指南
前端·vite
qq_398586541 小时前
浏览器中内嵌一个浏览器
前端·javascript·css·css3
Mapmost2 小时前
地图引擎性能优化:解决3DTiles加载痛点的六大核心策略
前端
San30.2 小时前
Ajax 数据请求:从 XMLHttpRequest 到现代前端数据交互的演进
前端·ajax·交互
西西西西胡萝卜鸡2 小时前
虚拟列表(Virtual List)组件实现与优化铁臂猿版(简易版)
前端·vue.js
宇余2 小时前
Unibest:新一代uni-app工程化最佳实践指南
前端·vue.js