在 TypeScript(以及 JavaScript)中,forEach
和 map
都是数组方法,但它们有重要的区别。了解这些区别对于编写高效和正确的代码至关重要。
1. 基本区别 #
特性 | forEach | map |
---|---|---|
返回值 | undefined | 新数组 |
是否修改原数组 | 可能(取决于回调函数) | 不修改原数组 |
链式调用 | 不支持(返回 undefined) | 支持(返回数组) |
用途 | 遍历执行操作 | 转换数组元素 |
2. 使用示例 #
forEach 示例 #
// 使用 forEach 遍历数组
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
numbers.forEach(num => {
sum += num;
console.log(num); // 执行副作用操作
});
console.log(sum); // 15
console.log(numbers); // [1, 2, 3, 4, 5] - 原数组不变
map 示例 #
// 使用 map 转换数组
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => {
return num * 2; // 返回新值
});
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // [1, 2, 3, 4, 5] - 原数组不变
3. 性能差异 #
// 性能测试示例
const largeArray = Array.from({length: 1000000}, (_, i) => i);
console.time('forEach');
largeArray.forEach(item => {
// 执行操作但不返回任何内容
});
console.timeEnd('forEach');
console.time('map');
const newArray = largeArray.map(item => {
return item; // 返回新值
});
console.timeEnd('map');
通常情况下,forEach
比 map
稍微快一些,因为它不需要创建和返回新数组。
4. 使用场景对比 #
适合使用 forEach 的场景 #
// 1. 执行副作用操作
const users = [{name: 'Alice'}, {name: 'Bob'}, {name: 'Charlie'}];
users.forEach(user => {
console.log(user.name); // 只是打印,不需要返回值
});
// 2. 修改外部变量
let total = 0;
const prices = [10, 20, 30];
prices.forEach(price => {
total += price;
});
// 3. 操作DOM元素
const elements = document.querySelectorAll('.item');
elements.forEach(el => {
el.classList.add('active');
});
适合使用 map 的场景 #
// 1. 转换数组元素
const numbers = [1, 2, 3];
const squared = numbers.map(n => n * n); // [1, 4, 9]
// 2. 提取对象属性
const users = [
{id: 1, name: 'Alice'},
{id: 2, name: 'Bob'}
];
const names = users.map(user => user.name); // ['Alice', 'Bob']
// 3. 链式操作
const result = numbers
.map(n => n * 2) // [2, 4, 6]
.filter(n => n > 3) // [4, 6]
.map(n => n + 1); // [5, 7]
5. 高级用法 #
使用索引参数 #
// forEach 和 map 都接收索引作为第二个参数
const fruits = ['apple', 'banana', 'cherry'];
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
const indexedFruits = fruits.map((fruit, index) => {
return `${index}-${fruit}`;
});
// ['0-apple', '1-banana', '2-cherry']
使用 this 参数 #
// 两者都可以指定回调函数中的 this 值
const multiplier = {
factor: 2,
multiply(numbers: number[]) {
return numbers.map(function(n) {
return n * this.factor;
}, this); // 指定 this
}
};
console.log(multiplier.multiply([1, 2, 3])); // [2, 4, 6]
6. 注意事项 #
提前终止:两者都不能使用
break
或continue
,但可以使用return
跳过当前迭代(在forEach
中)或返回特定值(在map
中)。异步操作:
// 两者都不适合异步操作,但可以使用 Promise.all 与 map 结合 async function processArray(array: number[]) { // 错误的做法 - 不会等待异步操作完成 array.forEach(async num => { await someAsyncOperation(num); }); // 正确的做法 - 使用 Promise.all 和 map await Promise.all(array.map(async num => { return await someAsyncOperation(num); })); }
稀疏数组:两者都会跳过稀疏数组中的空位。
总结 #
- 使用
forEach
当你只是想遍历数组并执行一些操作(副作用),而不需要返回值。 - 使用
map
当你需要基于原数组创建一个新数组,转换每个元素。 map
返回一个新数组,可以链式调用其他数组方法,而forEach
返回undefined
。- 在性能敏感的场景中,如果不需要创建新数组,
forEach
可能比map
更高效。
选择正确的方法可以使代码更清晰、更高效,并避免不必要的内存分配。