LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

【JavaScript】为什么 map 和 forEach 比 for 循环还快?到底该怎么用?

admin
2025年10月20日 10:20 本文热度 169

前言

在我们日常的前端开发中,循环无处不在。从简单的数组遍历到复杂的数据处理,循环都是我们必不可少的条件。

很多朋友在开发时也曾纠结过:到底该用for循环还是forEach?map和filter性能会不会很差?

而且不同浏览器、不同的环境下,循环性能结果差异也很大!甚至同一段代码每次运行的结果都不一样。

下面我们来测试一下。

测试环境和方法

我用的是Chrome浏览器最新版本,首先准备了一个包含10万个数字的数组,分别用不同的方法进行遍历操作。

// 测试数据
const
 testArray = Array.from({ length: 100000 }, (_, i) => i);

实际测试结果

经过多次测试取的平均值如下:

for循环: 2.8740234375 ms
for...of: 4.251953125 ms
forEach: 1.39404296875 ms
map: 1.31396484375 ms
filter: 1.43896484375 ms
reduce: 2.56591796875 ms

什么?map和forEach比for循环还快?

没错!这个结果确实颠覆了很多人的传统认知。

为什么高阶函数反而更快?

1. 引擎的内联优化
现代JavaScript引擎(如V8)能够把简单的回调函数内联处理,完全消除函数调用开销。

// 像这样的简单回调
const
 result = array.map(x => x * 2);

// 引擎可能会优化成类似这样

const
 result = new Array(array.length);
for
 (let i = 0; i < array.length; i++) {
    result[i] = array[i] * 2; // 直接内联,无函数调用
}

2. 提前编译优化
map的行为是可预测的:接收数组,返回新数组。
引擎可以提前准备内存和优化执行路径。

3. 隐藏类优化
高阶函数的使用模式更一致,有利于V8引擎的隐藏类优化机制。

传统for循环为什么相对比较慢?

// 传统for循环
for
 (let i = 0; i < array.length; i++) {
    // 每次循环都要:

    // 1. 检查数组长度(可能变化)

    // 2. 检查索引边界

    // 3. 检查数组类型

    result += array[i];
}

// 对比map

array.map(item => item * 2);
// 引擎知道:这是纯转换操作,可以大胆优化

// 数组长度不变,类型确定,无副作用

各种循环方法

1. map方法 - 最快

console.time('map');
const
 doubled = testArray.map(item => item * 2);
console
.timeEnd('map');

性能: 约 1.31ms(最快!)

核心优势:

  • • 现代引擎中性能表现最佳
  • • 代码意图明确,纯函数式
  • • 返回新数组,无副作用
  • • 可链式调用

最佳使用场景:

// 数据转换
const
 userNames = users.map(user => user.name);
const
 pricesWithTax = prices.map(price => price * 1.1);

// 数据格式化

const
 displayData = rawData.map(item => ({
    id
: item.id,
    name
: item.name.toUpperCase(),
    price
: `¥${item.price}`
}));

2. forEach方法 - 平衡

console.time('forEach');
let
 sum = 0;
testArray.forEach(item => {
    sum += item;
});
console
.timeEnd('forEach');

性能: 约 1.39ms

优势:

  • • 性能接近map
  • • 函数式编程风格
  • • 不会意外修改外部变量(除非故意)
  • • 代码可读性好

适用场景:

// 执行副作用操作
items.forEach(item => {
    updateUI
(item);
    logToServer
(item);
});

// 简单的聚合计算

let
 total = 0;
prices.forEach(price => {
    total += price;
});

3. filter方法 - 过滤

console.time('filter');
const
 evens = testArray.filter(item => item % 2 === 0);
console
.timeEnd('filter');

性能: 约 1.44ms

特色:

  • • 专为过滤设计,语义清晰
  • • 性能优秀
  • • 返回新数组

4. reduce方法 - 数据聚合器

console.time('reduce');
const
 sum = testArray.reduce((acc, curr) => acc + curr, 0);
console
.timeEnd('reduce');

性能: 约 2.57ms

不可替代的场景:

// 复杂数据统计
const
 statistics = data.reduce((stats, item) => {
    stats.total += item.value;
    stats.max = Math.max(stats.max, item.value);
    stats.min = Math.min(stats.min, item.value);
    return
 stats;
}, { total: 0, max: -Infinity, min: Infinity });

// 数据分组

const
 grouped = orders.reduce((groups, order) => {
    const
 category = order.category;
    if
 (!groups[category]) groups[category] = [];
    groups[category].push(order);
    return
 groups;
}, {});

5. 传统for循环 - 依然强大

console.time('for循环');
let
 sum = 0;
for
 (let i = 0; i < testArray.length; i++) {
    sum += testArray[i];
}
console
.timeEnd('for循环');

性能: 约 2.87ms

不可替代的优势:

// 1. 可以提前退出
for
 (let i = 0; i < array.length; i++) {
    if
 (array[i] === target) {
        break
; // 找到就停止
    }
}

// 2. 可以控制循环方向

for
 (let i = array.length - 1; i >= 0; i--) {
    // 倒序处理

}

// 3. 可以控制步长

for
 (let i = 0; i < array.length; i += 2) {
    // 每隔一个元素处理

}

// 4. 可以同时处理多个数组

for
 (let i = 0; i < array1.length; i++) {
    const
 result = array1[i] + array2[i];
}

6. for...of循环 - 语法最美

console.time('for...of');
let
 sum = 0;
for
 (const item of testArray) {
    sum += item;
}
console
.timeEnd('for...of');

性能: 约 4.25ms

独特价值:

// 1. 遍历任何可迭代对象
for
 (const [key, value] of myMap) {
    console
.log(key, value);
}

// 2. 遍历DOM集合

for
 (const element of document.querySelectorAll('.item')) {
    element.classList.add('active');
}

// 3. 遍历字符串

for
 (const char of 'Hello') {
    console
.log(char);
}

性能波动的科学解释

为什么每次测试结果都有差异?

1. 垃圾回收(Garbage Collection)

// 测试前后可能触发GC,影响计时准确性
const
 temp = new Array(100000); // 可能触发GC

2. 即时编译(JIT Compilation)

  • • 首次执行:解释执行
  • • 多次执行:编译优化
  • • 热点代码:深度优化

3. 系统资源波动

  • • 其他应用程序占用CPU
  • • 内存压力变化
  • • 浏览器标签页活动

4. 优化去优化循环

// 如果引擎发现优化假设错误,会"去优化"
function
 process(array) {
    // 如果array类型变化,会导致去优化

    return
 array.map(x => x * 2);
}

场景使用指南

场景1:数据转换管道

// 推荐:map/filter链式调用
const
 activePremiumUsers = users
    .filter(user => user.isActive && user.isPremium)
    .map(user => ({
        email
: user.email,
        displayName
: `${user.firstName} ${user.lastName}`.trim()
    }))
    .filter(user => user.displayName.length > 0);

场景2:搜索和提前退出

// 推荐:for循环(唯一选择)
function
 findUserById(users, id) {
    for
 (let i = 0; i < users.length; i++) {
        if
 (users[i].id === id) {
            return
 users[i]; // 找到立即返回
        }
    }
    return
 null;
}

场景3:复杂数据聚合

// 推荐:reduce
const
 salesReport = orders.reduce((report, order) => {
    const
 date = order.createdAt.split('T')[0];
    report.dates[date] = (report.dates[date] || 0) + order.amount;
    report.total += order.amount;
    report.count++;
    return
 report;
}, { dates: {}, total: 0, count: 0 });

场景4:DOM操作

// 推荐:forEach 或 for...of
document
.querySelectorAll('.button')
    .forEach(button => {
        button.addEventListener('click', handleClick);
    });

// 或者

for
 (const button of document.querySelectorAll('.button')) {
    button.addEventListener('click', handleClick);
}

场景5:性能极致要求

// 推荐:优化版for循环
function
 processLargeArray(array) {
    const
 result = new Array(array.length);
    // 缓存长度,避免重复查询

    for
 (let i = 0, len = array.length; i < len; i++) {
        result[i] = expensiveOperation(array[i]);
    }
    return
 result;
}

高级性能优化技巧

1. 合理使用循环链

// 多次循环,性能差
const
 activeUsers = users.filter(u => u.active);
const
 emails = activeUsers.map(u => u.email);
const
 validEmails = emails.filter(e => isValidEmail(e));

// 单次循环,性能好

const
 validEmails = users
    .filter(u => u.active)
    .map(u => u.email)
    .filter(e => isValidEmail(e));

2. 大数据集分块处理

async function processLargeDataset(data, chunkSize = 1000) {
    const
 results = [];
    
    for
 (let i = 0; i < data.length; i += chunkSize) {
        const
 chunk = data.slice(i, i + chunkSize);
        
        // 使用微任务避免阻塞UI

        await
 new Promise(resolve => {
            queueMicrotask
(() => {
                results.push(...processChunk(chunk));
                resolve
();
            });
        });
        
        // 或者使用requestIdleCallback

        // await new Promise(resolve => {

        //     requestIdleCallback(() => {

        //         results.push(...processChunk(chunk));

        //         resolve();

        //     });

        // });

    }
    
    return
 results;
}

3. 类型一致性优化

// 类型不一致,影响优化
const
 array = [1, 2, '3', 4]; // 混合类型

// 类型一致,便于优化

const
 array = [1, 2, 3, 4]; // 单一类型

总结

场景
推荐方法
理由
数据转换
map
性能最佳,语义明确
数据过滤
filter
专为过滤设计
数据聚合
reduce
表达能力最强
提前退出
for
唯一支持break的方法
可迭代对象
for...of
最通用的遍历方式
简单遍历
forEach
平衡性能和可读性
极致性能
优化for
手动优化缓存长度

黄金法则

  1. 1. 默认选择map/filter/reduce - 在大多数情况下性能更好,代码更清晰
  2. 2. 需要提前退出时用for循环 - 这是它的独特优势
  3. 3. 处理可迭代对象用for...of - 最通用的遍历方式
  4. 4. 保持类型一致性 - 帮助引擎更好优化
  5. 5. 避免不必要的函数创建 - 减少内存分配

写出人类能读懂、机器能优化的代码,才是真正的优秀代码!

希望这篇文章能帮助你在日常开发中做出更明智的选择。


该文章在 2025/10/20 10:21:44 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved