🎼 前言
希望这篇文章对你来说都能是废话。
面试前端候选人的时候,我喜欢问两个方面,一是基础知识,二是代码习惯。我认为,只有基础知识扎实的人才会对代码中的臭味敏感,而代码习惯好的才会有意识地去避免代码发臭。
而数组的几个遍历方法的用法,是我必问的基础知识。
JS 的数组有多少原生用于遍历方法?每个方法的作用什么?你是否知道什么情况下该用那个方法最合适?
我见过很多前端新人,甚至不少顶着「资深」、「专家」头衔的同学,在数组遍历方法的使用上有着各种各样的问题,让读代码的我气不打一处来:
- 有凡事求告 lodash 的(可能完全不知道原生的 Array 就可以做到)
- 有拿 map 当 forEach 用的
- 甚至有拿 filter 当 forEach 的
- 有认为在 forEach 里
return
会跳出循环的 - 有不知道 reduce 怎么用的
其中,拿 map 当 forEach 用是我见过最多的问题(我敢判断他纯粹是英语差)。
map 的效率一定是比 forEach 差的,因为它还要创建新数组,往新数组里插数据。但我不拿效率说事,对于目前的电脑性能来说,数据量没有达到一定的程度,这点效率消耗用户是完全感知不到的。我只论语义性,拿 map 当 forEach 使,跟你跟我说给我点外卖,却自己刷了半天抖音一个道理。
适合读者
- 前端开发
- 不清楚数组有多少遍历方法的
- 瞎用数组遍历方法,随心所欲、毫无章法的
编辑历史
日期 | 版本说明 |
---|---|
2023/09/08 | V1 |
🎗️ 完全 / 非完全遍历
数组的遍历方法,是指这个方法接收一个回调函数作为参数,这个回调函数 起码 会接收到当前值、当前位置和数组本身作为参数。
跟 for
循环可以 break
不一样,数组遍历方法是否能够跳出循环,是不能在语法层面控制的。有的方法一定会在全部遍历完成后才结束,有的则有可以中途提前结束循环。
因此,遍历方法可以分为「完全遍历」和「非完全遍历」两种。
方法 | 完全遍历 |
---|---|
forEach | ✅ |
filter | ✅ |
map | ✅ |
some | ❌ |
every | ❌ |
find | ❌ |
findLast | ❌ |
findIndex | ❌ |
findLastIndex | ❌ |
reduce | ✅ |
reduceRight | ✅ |
🎋 遍历方法选择策略
如何选择合适的遍历方法,是体现你基础知识是否扎实,保证不让维护者在这方面唾弃你的代码的关键。
我之前带过几个人做项目,对 CR 中重复指出数组方法问题不厌其烦,于是我画了一个图给他们,让他们以后不会选合适的方法时对照着看:
假设原数组类型为 Array<T>
,则根据以下步骤选择方法:
- 仅遍历,不需要返回结果
- 返回结果类型依然为
Array<T>
→ filter - 返回结果类型为非
T
数组,看结果长度与原数组是否相同
- 相同 → map
- 不同 → reduce / reduceRight
- 有关 → findIndex / findLastIndex
- 无关 → reduce / reduceRight
- 返回结果类型为其他(字符串、对象等) → reduce / reduceRight
其中,forEach 本身就没有返回值;别的方法,除了 some 可以偶尔忽略返回值之外,若忽略其返回值,则代码一定有问题。
仅遍历,不需要返回结果
不需要提前结束循环
毫无疑问用 forEach。
当然,这里也可以用 for
、for..in
、for..of
等。还是那句话,在数量级没有达到一定程度的情况下,谈 forEach 的性能没有这些好,是毫无意义的。
需要提前结束循环
用 some,忽略其返回值。
你说用 every 也可以?没错,但代码会难以理解,为了代码的可读性和一致性,请用 some。也就是说,当 every 的返回值被忽略的时候,说明代码有问题。
当然,这里也可以用带 break
的 for
、for..in
、for..of
。
返回结果类型依然为 Array<T>
毫无疑问用 filter。
返回结果类型为非 T
数组
结果长度与原数组相同
一个数组生一个不同类型,相同长度的数组,毫无疑问用 map。
结果长度与原数组不同
一个数组生一个不同类型,且不同长度的数组,用 reduce / reduceRight。
返回结果类型为 T | undefined
其实就是「找到一个元素」或者「找不到」,用 find / findLast。
如果你喜欢 .filter(...)[0]
,那么恭喜你,基础知识不过关。
返回结果类型为 boolean
你说 includes?很好,能用的时候用它,但它严格来说不是本文所说的带回调的「遍历」方法。
再一句,includes 比 arr.indexOf(o) >= 0
要好的多。
返回结果类型为 number
跟元素位置有关
找元素的位置,用 findIndex / findLastIndex。
你又说 indexOf / lastIndexOf?又很好,能用就用,但它们又严格来说不是本文所说的带回调的「遍历」方法。
跟元素位置无关
比如,把所有元素对象中的某个数值相加得到一个总数,用 reduce / reduceRight。
返回结果类型为其他
用 reduce / reduceRight。
🧮 来点统计
以下是我的一个项目下的统计数据,非权威数据。
map 真的用的很多,尤其是在有接口数据处理的时候。
🪭 写在最后
虽然,这篇文章对多数人来说都是废话,可我还是希望作为前端开发,可以沉下心来好好夯实基础,优化变成习惯。
如果,你看了这篇文章觉得毫无收获,那么恭喜你。