浅谈JavaScript中的for、forEach、map
相信大家在日常编码中经常会涉及到各种对数组的操作,其中避免不了的需要遍历数组,那么常用的三种循环遍历数组的方法for、forEach、map他们有什么区别呢?在使用时,我们又应该如何去选择使用哪种方法呢。
一、for
for语句用于创建一个循环,它包含了三个可选的表达式,这三个表达式被包围在圆括号之中,使用分号分隔,后跟一个用于在循环中执行的语句。
这里使用mdn的解释:
js
for ([initialization]; [condition]; [final-expression])
statement
initialization
一个表达式 (包含赋值语句) 或者变量声明。典型地被用于初始化一个计数器。该表达式可以使用 var
或 let
关键字声明新的变量,使用 var
声明的变量不是该循环的局部变量,而是与 for
循环处在同样的作用域中。用 let
声明的变量是语句的局部变量。该表达式的结果无意义。
condition
一个条件表达式被用于确定每一次循环是否能被执行。如果该表达式的结果为 true,statement
将被执行。这个表达式是可选的。如果被忽略,那么就被认为永远为真。如果计算结果为假,那么执行流程将被跳到 for
语句结构后面的第一条语句。
final-expression
每次循环的最后都要执行的表达式。执行时机是在下一次 condition
的计算之前。通常被用于更新或者递增计数器变量。
statement
只要condition
的结果为 true 就会被执行的语句。要在循环体内执行多条语句,使用一个块语句
({ ... }
)来包含要执行的语句。没有任何语句要执行,使用一个空语句
(;
)。
注意: for
循环中的三个表达式都是可选的,但分号是必须的。
二、forEach
forEach(callbackFn, thisArg)
作用: 遍历数组的每个元素,并对他们执行提供的函数
参数:
callbackFn
:必需,用来测试每个元素的函数,接受三个参数:element(当前遍历的元素),index(当前元素的索引),array(原数组)。thisArg
:可选,执行回调函数时使用的this值。
返回值: 无
示例:
js
const array = [1, 2, 3];
array.forEach((item) => console.log(item)); // 输出:
// 1
// 2
// 3
forEach
方法是一个迭代方法,他会按照数组的索引升序的为数组中的每一个元素执行提供的callbackFn
函数,thisArg
为可选参数,他表示执行callbackFn
时的this
指向,需要注意的是 ,当使用箭头函数时,thisArg
的参数是无意义的,因为箭头函数没有自己的this
绑定。
注意: 除非抛出异常,否则没有办法终止forEach
的循环,如果有终止或跳出循环的需求,应更换循环方法,如for
,for...of
等。forEach
的返回值始终是undefined
并且不会修改原数组,但是callbackFn
函数中是可以更改原数组的,但在第一次调用callbackFn
时,数组的长度已经保存,也就是说,callbackFn
不会访问超出数组初始长度的任何元素,即使他是在callbackFn
中添加的元素。已经访问过的索引的更改不会导致 callbackFn
再次调用它们。如果 callbackFn
更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn
的值将是在访问该元素时的值。已经被删除的元素不会被访问。
示例:
js
const arr = [1, 2, 3, 4, 5];
arr.forEach((item) => {
if (item == 2) {
arr.splice(2, 0, 6);
}
console.log(item); //1 //2 //6 //3 //4
});
const arr = [1, 2, 3, 4, 5];
arr.forEach((item) => {
if (item == 2) {
arr.shift();
}
console.log(item); //1 //2 //4 //5
});
const arr = [1, 2, 3, 4, 5];
arr.forEach((item) => {
if (item == 2) {
arr.splice(2, 1);
}
console.log(item); //1 //2 //4 //5
});
当上述情况并发的时候,往往会出现难以理解的代码,所以在一般情况下,应该尽量避免。
三、map
map(callbackFn, thisArg)
作用: 创建一个新数组,其中包含对原数组中的每个元素调用提供的函数的结果。
参数:
callbackFn
:必需,用来测试每个元素的函数,接受三个参数:element(当前遍历的元素),index(当前元素的索引),array(原数组)。thisArg
:可选,执行回调函数时使用的this值。
返回值: 由函数的返回值组成的新数组。
示例:
js
const array = [1, 2, 3];
const squaredArray = array.map((element) => element * element);
console.log(squaredArray); // [1, 4, 9]
map
方法和forEach
方法有些相似,他也是一个迭代方法,同样也是对原数组的每个元素都调用一次callbackFn
函数,但不同的是forEach
方法没有返回值,而map
方法会创建一个新数组,这个数组由每个元素执行callbackFn
后的值组成,也就是说,map
总是需要return
的。
同样的,map
方法在使用时也应该避免并发出现forEach
方法中提到的那些注意事项。
使用map
时,在某些情况下,使用的callbackFn
可能不止会接收一个参数,而我们可能在日常使用中习惯了只传递一个参数,这种情况下,可能会导致一些难以理解的问题,mdn上给出了一个很好的例子。
示例:
js
let arr = ["1", "2", "3"].map(parseInt);
console.log(arr); //[1,NaN,NaN]
在这里,我们理想中的情况应该是输出[1,2,3]
,但是结果却是[1,NaN,NaN]
。这里的原因其实很简单,我们习惯parseInt
只传递一个参数,但实际上parseInt
会接收两个参数string
和radix
,而map
方法传递了三个参数元素
、索引
和数组
。parseInt
会忽略第三个参数,但不会忽略第二个参数,就会导致第二个元素和第三个元素调用parseInt
方法时会以1和2为基数来进行转换,那么结果自然是NaN
。
当然,这种情况也是非常容易避免的,我们只需指定传入的参数是什么就好了:
js
let arr = ["1", "2", "3"].map((item) => parseInt(item));
console.log(arr); //[1,2,3]
四、如何选择使用哪种方法
简单总结一下,当需要精确控制循环条件和循环步进的情况,想要在适当的时候终止或跳出循环时应选择for
循环。for
循环可以使用return
跳出整个循环,并结束这个方法(包括这个方法中for
循环后面的所有内容都不会再执行),break
来跳出整个循环,但不会结束这个方法(会继续执行这个方法中for
循环后面的内容),continue
来跳过当前循环,继续执行下一次循环(不执行for
循环中continue
后面的内容,直接开始下一次循环)。for
循环拥有更高的性能和更好的可控性,但写起来不如forEach
和map
方法方便简洁。
当需要遍历数组中的每个元素,并进行一些副作用操作时,应选择forEach
方法。forEach
方法无法中止或跳出循环(不建议以抛出错误的方法中止循环),但可以使用return
来跳过当前循环,继续执行下一次循环。
当需要返回值组成一个新的数组时,应当选用map
方法。但是map
方法是无法中止或跳出循环,也无法跳过当前循环的。
由此看来,forEach
和map
还是有一定的局限性的,在遇到这些问题的时候,应根据需要选择合适的循环方法,如需要能够终止循环,可以选择some
方法和every
方法。想要能够跳过当次循环继续下一次循环可以使用for...of
方法等。 (JavaScript数组方法大合集)