js遍历那些事儿
次访问
本文总结js中的各种常用遍历。
基本用法
for
最基本的遍历写法,通过索引来遍历。
1 | let arr = [1, 2, 3] |
forEach
写法比for简便,遍历所有元素,不返回任何值。
1 | let arr = [1, 2, 3] |
map
映射,常用于基于原数组返回新数组(不改变原数组)。
1 | let arr = [1, 2, 3] |
filter
筛选,常用于过滤原数组来产生新数组,不改变原数组。
1 | let arr = [1, 2, 3] |
以上4种遍历基本满足大部分需求,下面看一些用的较少的遍历方法。
reduce
将数组中的元素逐个进行处理,并将它们合并为一个值。功能十分强大,回调函数可以进行各种复杂的操作,包括条件判断、对象构建等。
语法:
1 | array.reduce(function(total, currentValue, currentIndex, arr), initialValue) |
- callback (执行数组中每个值的函数,包含四个参数)
- total- 必需 (初始值, 或者计算结束后的返回值)
- currentValue - 必需 (数组中当前被处理的元素)
- currentIndex - 可选 (当前元素在数组中的索引)
- arr - 可选 (调用 reduce 的数组)
- initialValue - 可选 (作为第一次调用 callback 的第一个参数,如果不提供,第一次回调会使用数组的第一个元素)
示例(最简单的累加):
1 | let arr = [1, 2, 3] |
比如我们要统计字符串中每个字母出现的次数:
1 | const arrString = 'abcdaabc' |
every
条件都满足返回true。
1 | let arr = [1, 2, 3] |
some
有一个条件满足返回true。
1 | let arr = [1, 2, 3] |
for…in
主要用于遍历对象的可枚举属性(还会遍历原型链上的可枚举属性)。
遍历数组(不推荐,会遍历数组的所有可枚举属性,包括非索引属性和原型链上的属性):
1 | let arr = [1, 2, 3] |
遍历对象:
1 | let obj = { a: 1, b: 2, c: 3 } |
for…of
用于遍历可迭代对象(例如 Array, Map, Set, String, TypedArray,NodeList以及其他 DOM 集合,arguments 对象等)的可迭代属性(不可迭代属性会被忽略)。
1 | const arr = [1, 2, 3]; |
for...in
和for...of
的区别:
1 | Object.prototype.objCustom = function () {} |
可以看到,主要有3点不同:
- 1.
for...in
遍历key,for...of
遍历value - 2.
for...in
遍历的是可枚举属性,for...of
遍历的是可迭代属性 - 3.对于array的不可迭代元属性
objCustom
、arrCustom
和实例属性foo
,在for...of
循环中都被忽略
Object.keys,values,entries
对于普通对象(需要注意和map的区别):
- Object.keys(obj):返回一个包含该对象所有的键的数组。
- Object.values(obj):返回一个包含该对象所有的值的数组。
- Object.entries(obj):返回一个包含该对象所有 [key, value] 键值对的数组。
1 | let user = { |
Object.entries把 obj 变成由键/值对组成的数组,然后使用 Object.fromEntries可以将结果转回成原来的对应
1 | let user = { |
中断循环
中断循环,推荐使用break,continue不退出循环,只跳过当前循环,if条件判断替换continue,可读性更高。
for循环(for
/for...in
/for...of
)可通过break退出循环。
1 | let arr = [1, 2, 3] |
总结,有
for
关键字时,可以通过break退出循环,forEach可以通过throw Error的方式退出循环(但不推荐这样写,推荐用for)。
异步
当遍历碰到async
、 await
、 Promise
时,又该怎么写呢?
1 | let arr = [20, 10, 30] |
上面有个数组arr
和耗时操作sleep
,如果没有耗时操作,就是下面同步的写法:
1 | const syncRes = arr.map((i) => { |
如果有耗时操作,像下面这样,怎么保持输出还是[20, 10, 30]
呢?
1 | await sleep(i) |
最简单的方法可以用for循环(for
/for...in
/for...of
都一样):
1 | let asyncRes1 = [] |
map
可以像这样Promise.all(arr.map(async (...) => ...))
:
1 | const asyncRes = await Promise.all(arr.map(async (i) => { |
如果不用Promise.all
,就是下面的效果:
1 | const asyncRes = arr.map(async (i) => { |
碰到reduce时async (prev, cur) => await prev
:
1 | const asyncRes = await arr.reduce(async (prev, cur) => { |
在forEach
、filter
等其他循环中需要使用异步,先用map
、reduce
、for...of
处理。
总结
- 基本用法要熟记特性,灵活选用
- 需要中断就用for(
for
/for...in
/for...of
),使用break
退出 - 碰到异步用for(
for
/for...in
/for...of
)/map
/reduce