深聊JS数组中的forEach和map方法
在和团队分享Airbnb的eslint规则时,当我们讨论 no-param-reassign 的时候,在项目的代码中用到了forEach 方法,场景如下:
const demoFunc = (arr) => {
arr.forEach((item) = > {
item.name = 'coder';
});
}
因为改变了函数的形参,所以报了eslint的错误,这引起了我们对forEach方法的重视,同时也让我们想到了另一很类似的map方法,对于有追求的码农,当然要写篇文章深入剖析有关的知识。
forEach()和map()两个方法都是ECMA5中Array引进的新方法,主要作用都是对数组的每个元素执行一次提供的函数,但他们还是有区别的。
map()
map方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
const arr = [1, 2, 3];
const newArr = arr.map(item => item + 1);
console.log(arr); // [ 1, 2, 3 ]
console.log(newArr); // [ 2, 3, 4 ]
上面代码中,arr数组的所有成员依次执行参数函数,运行结果组成一个新数组返回,原数组没有变化。
forEach()
forEach方法与map方法很相似,也是对数组的所有成员依次执行参数函数。但是,forEach方法不返回值,只用来操作数据。
这就是说,如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法。
const arr = [1, 2, 3];
const newArr = arr.forEach(item => item + 1);
console.log(arr); // [ 1, 2, 3 ]
console.log(newArr); // undefined
可以看到,newArr为undefined,说明forEach方法不返回值。
注意,forEach方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用for循环。
forEach和map方法 能改变原数组吗?
可以看到上面的例子,对于数组元素为基础类型的数组,forEach和map方法 并不能改变原数组,但是当元素为对象时:
const arr = [{ name: 'coder' }, { name: 'coder' }, { name: 'coder' }];
const newArr = arr.forEach(item => item.name = 'newName');
console.log(arr);
// [ { name: 'newName' }, { name: 'newName' }, { name: 'newName' } ]
console.log(newArr); // undefined
可以看到,对于元素为对象的数组,再使用forEach()方法时,虽然依然没有返回值,但原数组的元素却是可以改变的。
const arr = [{ name: 'coder' }, { name: 'coder' }, { name: 'coder' }];
const newArr = arr.map(item => item.name = 'newName');
console.log(arr);
// [ { name: 'newName' }, { name: 'newName' }, { name: 'newName' } ]
console.log(newArr);
// [ 'newName', 'newName', 'newName' ]
可以看到,对于元素为对象的数组,再使用map()方法时,原数组的元素也是可以改变的,但是返回的新数组却不是由对象元素组成的。
结语
可以看到,即使是我们常用到的forEach和map方法,也是有很多可以深入的地方,特别是当数组元素为引用类型时,是可以改变数组本身的。
而且map()方法返回的新数组和原数组也是有区别的,可见eslint的代码检查则能提醒我们要清楚知道自己所写代码的意图,防止未知错误的发生。