前言
起因是这样的,面试官先问我讲讲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?
- 使用forEach方法:
javascript
const mySet = new Set([1, 2, 3, 4, 5]);
mySet.forEach(element => {
console.log(element);
}); // 输出1 2 3 4 5
- 使用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
- 转换为数组后遍历:
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...in
和for...of
啦,for...of
和Set
、Map
都是es6新出的,需要具有迭代器属性才能用for...of
进行遍历,此外String
和Array
也是具有迭代属性可以被for...of
进行遍历,普通对象没有迭代属性会报错.
for...in
可以专门用来遍历对象,它可以遍历可枚举属性,去得到对象的键,而数组没有对象所谓的键所以会默认用下标来当成键遍历, 遍历数组会输出下标值。
分不清可以这样想,我们平时写项目的时候遍历一个对象不都是 v-for:"item in data" 吗,这就是for...in
的遍历,剩下的数组呀,Set、Map 啦不就是给剩下的for...of
遍历啦。
最后关于迭代器这个属性,字节前几年也专门考过这一题,感兴趣可以去看看这篇:这是来自字节俩道面试题。。。汗颜