JavaScript 中有哪些阵列 (Array) 的遍历方法(for loop, for...in, for…of, forEach, map, filter, every, some)
2023年2月1日
在考察 JavaScript 的基础能力面试中,经常会问到遍历阵列的方法。本篇文章集合了,JavaScript 当中最常使用到的阵列遍历方法,包括 for
回圈、阵列的原生方法(forEach
, map
…),还有面试当中常见的阵列遍历考题。
本篇主要分成两大段落:
- 遍历阵列的 8 种方法
for...of
和for...in
有什么差别?
遍历阵列 8 种方法
for loop
- 好处是可以使用
break
结束循环或使用continue
跳出当前回圈 - 命令式的写法比较冗长
let arr = [0, 1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
if (i > 2) break;
console.log(arr[i]);
}
// 0
// 1
// 2
for…of
- ES6 之后出现的语法,与
for
回圈相比写法较简洁 - 与
for
回圈一样,可以搭配break
、continue
和return
使用
const arr = ["apple", "orange", "banana"];
for (const item of arr) {
if (item === "orange") continue;
console.log(item);
}
// apple
// banana
for...in
- 遍历的是
键值 (key)
,而在阵列中的键值 (key)
就是索引 (index)
const arr = ["apple", "orange", "banana"];
for (const item in arr) {
console.log(item);
}
// 0
// 1
// 2
以下介绍阵列的原生方法
forEach
会针对每一个元素执行提供的函式
声明式的写法较为精简,但是不能使用
break
和continue
语法跳出循环,如下图forEach
方法只会遍历原阵列,并不会回传一个新阵列。所以如果需要从一个旧阵列中建构出另一个新阵列,应该使用map
方法
map
- 针对每一个元素执行提供的函式,并回传新阵列
const arr = [1, 2, 3, 4];
const newArr = arr.map((x) => x + 1);
console.log(newArr);
// [2,3,4,5]
console.log(arr);
// [1,2,3,4] // 原阵列不会被改变
filter
- 创建回传一个新阵列,并过滤掉没有通过所提供函式的元素
const arr = [19, 2, 13, 40];
const newArr = arr.filter((x) => x > 18);
console.log(newArr);
// [19, 40]
console.log(arr);
// [19, 2, 13, 40] // 原阵列不会被改变
every
- 会测试阵列中每一个元素是否通过提供的函式,最终返回一个
布林值(Boolean)
- 如果每个元素都通过,最终会回传
true
,但若其中有一个元素测试值为false
,那就会提前结束,并回传false
- 与
some
的差别在于,用every
方法,需要阵列当中每一个都通过测试的函式,最终才会回传true
[12, 5, 8, 130, 44].every((x) => x > 10); // false
[12, 54, 18, 130, 44].every((x) => x > 10); // true
some
- 方法类似于
every
,会测试阵列每一个元素是否通过提供的函式,若其中有一个元素测试值为true
,那就会提前结束,并回传true
- 与
every
的差别在于,用some
方法,只要阵列当中有一个元素通过测试函式,就会回传true
[2, 5, 8, 1, 4].some((x) => x > 10); // false
[12, 5, 8, 1, 4].some((x) => x > 10); // true
延伸题:哪些方法会改变原阵列?
以上提拱的几种阵列方法,预设上并不会直接改变原阵列,除非我们传入的函式针对原阵列做一些处理。
延伸题:哪些方法会回传新的阵列?
map
与 filter
会回传新的阵列,因此如果是在 React 等要求写不可变 (immutable) 的程式码时,经常需要用到这两个方法。
延伸题:请问 for
循环 和 forEach
的差别?
- 写法上差异,
forEach
较为简洁 forEach
不能提早结束整遍历,也不能搭配break
和continue
语法使用,使用return
也会被忽略
延伸题:请问 for...of
和 for...in
的差别?
for...of
遍历的是阵列中的元素值 (value)
;而for...in
是遍历的是键值 (key)
,换句话说for... in
是遍历阵列中的索引 (Index)
var arr = [10, 20, 30]; for (let value of arr) { console.log(value); } //10 20 30 var arr = [10, 20, 30]; for (let value in arr) { console.log(value); } //0 1 2
大部分情况,
for...in
会被拿来遍历物件,但并不建议使用for...in
遍历阵列,主要原因有以下 4 点:当阵列中有空项目时,使用
for...in
方法会忽略该项目。let a = ["a", , "c"]; for (let x in a) { console.log(x); } // 0 2 for (let x of a) { console.log(x); } // a undefined c
for... in
会检查对象的属性是否 enumerable ,如果 true ,会把这些属性名称全部迭代出来,因为某些 JavaScript 套件可能会在 Array 原型上建立方法,此时如果使用for...in
方法可能会操作到不是存在于阵列中的值。 但是用for...of
可以避免这问题。Array.prototype.foo = 1; let a = [1, 2, 3]; for (let x in a) { console.log(x); } // 0 1 2 foo
for...in
遍历的键值(key)虽然是Index
,但for...in
会以 String 型别作为键值(key),因此我们如果拿Index
去做运算,可能会造成预期外的结果。let a = [1, 2, 3]; for (let x in a) { console.log(x + 1); } // 01 11 21
for...in
的遍历并不保证顺序是正确的,这样的状况在阵列上非常难使用,因为当我们想迭代阵列中的值时,就是希望顺序能够正确。而for...of
会先检查对象[Symbol.iterator]
这个属性,接着使用[Symbol.iterator].next()
一个个迭代出值,来确保顺序正确。但由于 Object 并不具备[Symbol.iterator]
这个属性,所以for...of
并不能使用在 Object 上。