关联阅读推荐
JavaScript中数组方法,map和forEach的区别
| 对比维度 | map() |
forEach() |
|---|---|---|
| 返回值 | 返回一个新数组,长度与原数组相同 | 返回 undefined |
| 主要用途 | 转换数组数据,对每个元素进行处理并收集结果 | 遍历数组,执行副作用操作(如打印、赋值、DOM操作) |
| 链式调用 | 支持链式调用(如 .map().filter()) |
不支持链式调用(返回undefined) |
| 是否改变原数组 | 不会改变原数组(除非回调函数内部修改) | 不会改变原数组(除非回调函数内部修改) |
| 性能 | 相对较慢(需要创建新数组并分配内存) | 相对较快(不创建新数组) |
| 回调参数 | (currentValue, index, array) |
(currentValue, index, array) |
| 空数组处理 | 返回空数组 | 返回 undefined,不执行回调 |
| 中断循环 | 不支持(无法break) | 不支持(无法break) |
| 适用场景 | 需要基于原数组得到一个新数组时 | 只需要遍历执行操作,不需要返回值时 |
代码示例对比
javascript
// map() 示例 - 转换数据
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
console.log(numbers); // [1, 2, 3, 4] (原数组不变)
// 链式调用示例
const result = numbers
.map(num => num * 2)
.filter(num => num > 4);
console.log(result); // [6, 8]
// forEach() 示例 - 执行操作
numbers.forEach((num, index) => {
console.log(`索引 ${index}: ${num}`);
});
// 输出:
// 索引 0: 1
// 索引 1: 2
// 索引 2: 3
// 索引 3: 4
// 错误用法示例
const wrong = numbers.forEach(num => num * 2);
console.log(wrong); // undefined
选择建议
-
使用
map()当:你需要基于原数组创建一个新的、转换后的数组 -
使用
forEach()当:你只需要遍历数组执行某些操作,不需要返回值
map需要显式返回的情况
示例
TypeScript
//使用通用函数优化默认图片显示
const columnList = computed(() => {
return props.list.map(column => {
addColumnAvatar(column, 50, 50)
//map返回修改后的新数组,这里需要显式返回是因为addColumnAvatar没有返回值,它直接修改了传入的 column 对象。
return column
})
})
通用函数
TypeScript
export function generateFitUrl(data: ImageProps, width: number, height: number, format = ['m_pad']) {
if (data && data.url) {
const formatStr = format.reduce((prev, current) => {
return current + ',' + prev
}, '')
data.fitUrl = data.url + `?x-oss-process=image/resize,${formatStr}h_${height},w_${width}`
}
}
//addColumnAvatar() 直接修改传入的对象(添加或修改 avatar 属性)
//没有返回值(返回 undefined)
export function addColumnAvatar(data: ColumnProps | UserProps, width: number, height: number) {
if (data.avatar) {
generateFitUrl(data.avatar, width, height)
} else {
const parseCol = data as ColumnProps
data.avatar = {
fitUrl: require(parseCol.title ? '@/assets/column.jpg' : '@/assets/avatar.jpg')
}
}
}
核心原则 :
map()的回调函数必须返回一个值 ,这个值会成为新数组对应位置的元素。如果回调函数没有返回值,新数组对应位置就是undefined。
为什么 map 中需要 return?
TypeScript
const columnList = computed(() => {
return props.list.map(column => {
addColumnAvatar(column, 50, 50); // 返回 undefined
return column; // 必须返回修改后的 column
});
});
如果不 return column:
TypeScript
const columnList = computed(() => {
return props.list.map(column => {
addColumnAvatar(column, 50, 50); // 没有返回值
// 隐式返回 undefined
});
});
// 结果: [undefined, undefined, undefined, ...]
优化方案
方案1:使用 forEach(更符合语义)
TypeScript
const columnList = computed(() => {
// 避免直接修改 props,创建副本
const listCopy = [...props.list];
listCopy.forEach(column => addColumnAvatar(column, 50, 50));
return listCopy;
});
方案2:修改函数,支持链式调用
TypeScript
// utils/avatar.ts
export function addColumnAvatar<T extends ColumnProps | UserProps>(
data: T,
width: number,
height: number
): T {
if (data.avatar) {
generateFitUrl(data.avatar, width, height);
} else {
const parseCol = data as ColumnProps;
data.avatar = {
fitUrl: require(parseCol.title ? '@/assets/column.jpg' : '@/assets/avatar.jpg')
};
}
return data; // 关键:返回修改后的对象
}
// component.vue
const columnList = computed(() =>
props.list.map(column => addColumnAvatar(column, 50, 50))
);
这样既保持了函数的原有逻辑,又让 map 的使用更加优雅,不需要显式 return column。
注意:箭头函数的隐式返回
javascript
// 显式返回(需要 return 关键字)
const fn1 = (param) => {
return param * 2;
};
// 隐式返回(省略 return 和大括号)
const fn2 = (param) => param * 2;
TypeScript
// 原始代码 - 需要显式返回
const columnList = computed(() => {
return props.list.map(column => {
addColumnAvatar(column, 50, 50);
return column; // 必须显式返回
});
});
// 如果尝试隐式返回,会出错
const columnList = computed(() => {
return props.list.map(column =>
addColumnAvatar(column, 50, 50) // 返回的是 addColumnAvatar 的结果(undefined)
);
});
// 结果: [undefined, undefined, undefined, ...]