likes
comments
collection
share

浅谈JavaScript中的for、forEach、map

作者站长头像
站长
· 阅读数 17

浅谈JavaScript中的for、forEach、map

相信大家在日常编码中经常会涉及到各种对数组的操作,其中避免不了的需要遍历数组,那么常用的三种循环遍历数组的方法for、forEach、map他们有什么区别呢?在使用时,我们又应该如何去选择使用哪种方法呢。

一、for

for语句用于创建一个循环,它包含了三个可选的表达式,这三个表达式被包围在圆括号之中,使用分号分隔,后跟一个用于在循环中执行的语句。

这里使用mdn的解释:

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值。

返回值:

示例:

const array = [1, 2, 3]; 
array.forEach((item) => console.log(item)); // 输出: 
// 1 
// 2 
// 3

forEach方法是一个迭代方法,他会按照数组的索引升序的为数组中的每一个元素执行提供的callbackFn函数,thisArg为可选参数,他表示执行callbackFn时的this指向,需要注意的是,当使用箭头函数时,thisArg的参数是无意义的,因为箭头函数没有自己的this绑定。

注意: 除非抛出异常,否则没有办法终止forEach的循环,如果有终止或跳出循环的需求,应更换循环方法,如forfor...of等。forEach的返回值始终是undefined并且不会修改原数组,但是callbackFn函数中是可以更改原数组的,但在第一次调用callbackFn时,数组的长度已经保存,也就是说,callbackFn不会访问超出数组初始长度的任何元素,即使他是在callbackFn中添加的元素。已经访问过的索引的更改不会导致 callbackFn 再次调用它们。如果 callbackFn 更改了数组中已经存在但尚未访问的元素,则传递给 callbackFn 的值将是在访问该元素时的值。已经被删除的元素不会被访问。

示例:

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值。

返回值: 由函数的返回值组成的新数组。

示例:

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上给出了一个很好的例子。

示例:

let arr = ["1", "2", "3"].map(parseInt);
console.log(arr); //[1,NaN,NaN]

在这里,我们理想中的情况应该是输出[1,2,3],但是结果却是[1,NaN,NaN]。这里的原因其实很简单,我们习惯parseInt只传递一个参数,但实际上parseInt会接收两个参数stringradix,而map方法传递了三个参数元素索引数组parseInt会忽略第三个参数,但不会忽略第二个参数,就会导致第二个元素和第三个元素调用parseInt方法时会以1和2为基数来进行转换,那么结果自然是NaN

当然,这种情况也是非常容易避免的,我们只需指定传入的参数是什么就好了:

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循环拥有更高的性能和更好的可控性,但写起来不如forEachmap方法方便简洁。

当需要遍历数组中的每个元素,并进行一些副作用操作时,应选择forEach方法。forEach方法无法中止或跳出循环(不建议以抛出错误的方法中止循环),但可以使用return来跳过当前循环,继续执行下一次循环。

当需要返回值组成一个新的数组时,应当选用map方法。但是map方法是无法中止或跳出循环,也无法跳过当前循环的。

转载自:https://juejin.cn/post/7278123169259536441
评论
请登录