如何遍历Array,Object,Set,Map?

110 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

数组的遍历

1. for 循环

要注意的是:for循环还有一个特别之处,设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

2. forEach

  • 不对未初始化的值进行任何操作(稀疏数组)
  • 没有办法中止或跳出 forEach() 循环

3. for...in

  • 不应该用于迭代一个关注索引顺序的数组
  • for...in会往array的原型链上寻找,则遍历的不只是数组内的元素,还有其新增的原型属性和索引。
let arr = [1, 2, 3]
for (let i in arr) {
  if (arr[i] > 2) {
    console.log('使用for...in循环得到的值:', i)
  }
}
// 使用for...in循环得到的值:2
//给数组的原型上增加一个方法
Array.prototype.method = function () {
  console.log('this is a method')
}
arr.name = 'zz'
for (let i in arr) {
    console.log(i)
}
// 0
// 1
// 2
// name
// method

4. for...of

for...of在可迭代对象(包括 ArrayMapSetStringTypedArrayarguments 对象等等)上创建一个迭代循环。

// item是数组元素 无法直接获取索引
for (let item of arr) {
    if (item > 2) {
        //return item
    }
}

那么如何获取索引呢?

  • 借用entries()
let arr = ['a','b','c']
 
for (let [index, item] of arr.entries()){
  console.log(index, item)
}
//0 "a"
//1 "b"
//2 "c"
  • 借用Map结构

先将数组转化为Map

let arr = [ 'a', 'b', 'c' ];
 
for( let [ index, item ] of new Map( arr.map( ( item, index ) => [ index, item ] ) ) ) {
    console.log( index, item );
 
}

5. findIndex()

返回第一个满足条件的元素下标

console.log(arr.findIndex(function(item){
  return item > 2
}))

6. 其他

everysomefind,像findindex一样,能提前终止循环。

对象的遍历

1. for...in

它以任意顺序迭代一个对象的除[Symbol]以外的可枚举属性,包括继承的可枚举属性。

如果你只要考虑对象本身的属性,而不是它的原型,那么使用 getOwnPropertyNames() 或执行 hasOwnProperty() 来确定某属性是否是对象本身的属性

2. 键值的遍历

  • 键值对的遍历Object.entries()

返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)

可以通过数组解构赋值方式访问键和值

const obj = { a: 1, b: 2, c: 3 };
for (const [key, value] of Object.entries(obj)) {
  console.log(`${key} ${value}`); // "a 1", "b 2", "c 3"
}

Map的转换
因为new Map() 构造函数接受一个可迭代的entries。借助Object.entriesObject转换为Map

var obj = { foo: "bar", baz: 42 };
var map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
  • 键的遍历

Object.keys()方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。

var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); 
// ['0', '1', '2']
  • 值的遍历

Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。

var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); 
// ['a', 'b', 'c']

Set 的遍历

1. forEach

使用回调函数进行处理

let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(key + ' : ' + value))

2. 键值对

keys(),values(),entries()返回的都是遍历器对象

let set = new Set(['one', 'two', 'three']);

for (let item of set.keys()) {
  console.log(item);
}
// one
// two
// three

for (let item of set.values()) {
  console.log(item);
}
// one
// two
// three

for (let item of set.entries()) {
  console.log(item);
}
// ["one", "one"]
// ["two", "two"]
// ["three", "three"]

Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values()方法。

Set.prototype[Symbol.iterator] === Set.prototype.values // true 

这意味着,可以省略values方法,直接用for...of循环遍历 Set

let set = new Set(['one', 'two', 'three']);

for (let x of set) {
  console.log(x);
}
// one
// two
// three

因为扩展运算符使用for...of 循环,所以可以结合扩展运算符使用。

let set = new Set(['red', 'green', 'blue']);
let arr = [...set];
// ['red', 'green', 'blue']

Map 的遍历

1. forEach

map.forEach(function(value, key, map) {
  console.log("Key: %s, Value: %s", key, value);
});

2. 键值对

const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

总结

  1. 灵活运用遍历需要掌握的不仅仅是一些上述提到的方法,前提需要掌握的是如SetMap的定义,MapObject的区别等;
  2. 了解一些包括迭代器,扩展运算符的内容,也能掌握数据的转化,如Map和数组的相互转化,而不需要死记硬背。