JavaScript中map和forEach的区别示例详解
作者:~嘻嘻哈哈~
一、本质区别
1. 返回值不同
map
返回回调函数对每个元素处理后的新数组。forEach
返回undefined
。
const arr = [1, 2, 3]; // [3, 6, 9] const arr_1= arr.map(item => item * 3); // undefined const arr_2= arr.forEach(item => item * 2);
2. 是否可链式调用
- 因
map
返回新数组,可链式调用,可用其他数组方法(如filter
、reduce
)。- 因
forEach
返回undefined,不可
链式调用。
const arr = [1, 2, 3]; // 可链式调用 [6, 9] arr.map(item => item * 3).filter(x => x > 3); // Cannot read property 'filter' of undefined arr.forEach(item => item * 3).filter(x => x > 3);
二、是否修改原数组
1. map
1. 当原数组元素是基本数据类型(如数字、字符串、布尔值),会生成新数组,不改变原数组。
const arr = [1, 2, 3]; const arr_1 = arr .map(item => item * 2); // [1, 2, 3] console.log(arr); // [2, 4, 6] console.log(arr_1);
2. 当原数组元素是引用数据类型(如对象、数组)时,会生成新数组,若回调函数中直接修改其元素属性,可改变原数组(因其元素是引用的)。
const names= [{ name: 'zhangsan' }, { name: 'lisi' }]; const names_1 = names.map(item=> { // 直接修改原对象属性 item.name = 'wangwu'; return item; }); // [{ name: 'wangwu' }, { name: 'wangwu' }] console.log(names); // [{ name: 'wangwu' }, { name: 'wangwu' }] console.log(names_1);
3. 当原数组元素是引用数据类型(如对象、数组)时,会生成新数组,若回调函数中创建新对象/数组,不改变原数组。
const names= [{ name: 'zhangsan' }, { name: 'lisi' }]; const names_1 = names.map(item=> ({ ...item, age: 21 })); // [{ name: 'wangwu' }, { name: 'lisi' }] console.log(names); //[{ name: 'zhangsan', age: 21 }, { name: 'lisi', age: 21 }] console.log(names_1);
4. 总结:map本身不改变原数组,若回调函数中对引用数据类型修改,可改变原数组。
2. forEach
1. 当原数组元素是基本数据类型(如数字、字符串、布尔值),不改变原数组。
const arr = [1, 2, 3]; arr.forEach(item => item *= 2); // [1, 2, 3] console.log(arr);
2. 当原数组元素是引用数据类型(如对象、数组)时,修改其元素属性,可改变原数组(因其元素是引用的)。
let names= [{ name: 'zhangsan' }, { name: 'lisi' }]; names.forEach((item, index, array) => { array[index] = 'wangwu'; // 或 item.name = 'wangwu' }); // [{ name: 'wangwu' }, { name: 'wangwu' }] console.log(names);
3. 当原数组元素是引用数据类型(如对象、数组)时,直接替换整个元素对象,不改变原数组(形参是原元素的副本)。
let names= [{ name: 'zhangsan' }, { name: 'lisi' }]; names.forEach(item => item = { name: 'xiaohong' }); // [{ name: 'zhangsan' }, { name: 'lisi' }]; console.log(names);
4. 总结:forEach,修改基本数据类型元素的值,或直接替换引用数据类型元素,不改变原数组。修改引用数据类型元素的属性,可改变原数组。
三、使用场景
map使用场景:需生成新数组(如数据转换、提取属性)或需链式调用其他方法时。
// [2, 4, 6] const arr = [1, 2, 3].map(x => x * 2);
forEach使用场景:无需新数组,需如打印、修改外部变量时。
let sum = 0; // 6 [1, 2, 3].forEach(item => sum += item);
四、forEach使用限制
1. 无法中断或跳过循环
不支持使用break或continue语句来中断或跳过循环,可通过for、for...of解决。或抛出异常解决。
// 抛出异常解决 let arr = [1, 2, 3]; try { arr.forEach(item => { if (item === 2) { throw('error'); } console.log(item); }); } catch(e) { console.log(e); } // 1 // error
2.不支持处理异步函数
forEach是同步方法,在其中执行异步函数,不会等待异步函数完成,会立即处理下一个元素,无法保证执行顺序,可通过for...of解决。
async function handleArr(arr) { arr.forEach(async item => { const res = await asyncData(item); console.log(res); }); console.log('end'); } function asyncData(x) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(x); }, 1000 * x); }); } handleArr([1, 2, 3]); // 预期 1 2 3 end // 实际 end 3 2 1
3.无法捕获异步函数中错误
即使异步函数在执行过程中抛出错误,forEach仍继续处理下一个元素,不会处理错误。
4. 遍历过程中更改索引无效
forEach中索引是自动管理的,每次遍历时自动递增,可通过for解决。
// 1. 试图直接更改索引 let arr = [1, 2, 3, 4]; arr.forEach((item, index) => { // 1 2 3 4 console.log(item); index++; }); // 2. 试图删除元素更改索引 let arr_1 = [1, 2, 3, 4]; arr_1.forEach((item, index) => { if (item === 2) { // 删除元素2 arr.splice(index, 1); } // [0, 1] [1, 2] [2, 4] console.log([index, item]); }); // [1, 3, 4] console.log(arr_1); // 3. for解决 let arr_2 = [1, 2, 3, 4]; for (let i = 0; i < arr_2.length; i++) { if (arr_2 [i] === 2) { // 删除元素2 arr_2.splice(i, 1); i--; } else { // 1 3 4 console.log(arr_2[i]); } } // [1, 3, 4] console.log(arr_2);
总结
到此这篇关于JavaScript中map和forEach的区别详解的文章就介绍到这了,更多相关JS中map和forEach区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!