JavaScript循环方法详解:如何选择最适合的遍历方式

更新日期: 2025-10-22 阅读: 62 标签: 遍历

前端开发中,循环是必不可少的工具。无论是处理数组数据还是操作dom元素,我们都需要使用循环。很多开发者经常纠结:该用for循环还是forEach?map和filter的性能会不会很差?

实际上,不同循环方法在不同场景下各有优劣。让我们通过实际测试来了解它们的性能特点。


性能测试对比

我在Chrome浏览器最新版本中进行了测试,使用包含10万个数字的数组:

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

测试结果如下(多次测试的平均值):

  • for循环: 2.87ms

  • for...of循环: 4.25ms

  • forEach: 1.39ms

  • map: 1.31ms

  • filter: 1.44ms

  • reduce: 2.57ms

这个结果可能让人惊讶:map和forEach居然比传统的for循环还要快!


为什么高阶函数性能更好

1. 引擎的内联优化

现代JavaScript引擎能够将简单的回调函数内联处理,消除函数调用开销:

// 这样的简单回调
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. 垃圾回收机制
测试前后可能触发垃圾回收,影响计时准确性。

2. 即时编译优化

  • 首次执行:解释执行

  • 多次执行:编译优化

  • 热点代码:深度优化

3. 系统资源波动
其他应用程序占用CPU、内存压力变化等都会影响结果。


实际应用场景指南

场景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();
            });
        });
    }
    
    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. 默认选择map/filter/reduce - 在大多数情况下性能更好,代码更清晰

  2. 需要提前退出时用for循环 - 这是它的独特优势

  3. 处理可迭代对象用for...of - 最通用的遍历方式

  4. 保持类型一致性 - 帮助引擎更好优化

  5. 避免不必要的函数创建 - 减少内存分配

写出既容易理解又能被引擎优化的代码,才是真正优秀的代码。根据具体场景选择合适的方法,不要盲目追求性能而牺牲代码的可读性。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/13034

js中循环总结,多种循环遍历的实现介绍(while,for,map,foreach等)

循环是所有语言最基础的语法。这篇文章将介绍avascript多种循环遍历的实现介绍,包括for ..in遍历方式 ,do..while,forEach,for…of,map等等,

JavaScript数组遍历:for、foreach、for in、for of、$.each、$().each的区别

JavaScript数组遍历的区别,推荐在循环对象属性的时候使用for in,在遍历数组的时候的时候使用for of;for in循环出的是key,for of循环出的是value;for of是ES6新引入的特性。修复了ES5的for in的不足;

PHP遍历二叉树

遍历二叉树,这个相对比较复杂。二叉树的便利,主要有两种,一种是广度优先遍历,一种是深度优先遍历。什么是广度优先遍历?就是根节点进入,水平一行一行的便利。

js数组、对象、字符串的遍历

数组遍历:for --使用变量将数组长度缓存起来,在数组较长时性能优化效果明显,forEach --ES5语法;对象遍历:for...in --以任意顺序遍历一个对象自有的、继承的、可枚举的

Js二叉树与多叉树的遍历

二叉树的顺序存储结构就是用一维数组存储二叉树中的各个结点,并且结点的存储位置能体现结点之间的逻辑关系。二叉树的遍历有三种方式,如下:

JS中map与forEach的用法

相同点:都是循环遍历数组中的每一项;每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组)

Js数组遍历方法总汇

for in语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作);For循环可以将代码块执行指定的次数。forEach按照原始数组元素顺序依次处理元素.

深度优先遍历,广度优先遍历实现对象的深拷贝 深度优先遍历

深度优先遍历(Depth-First-Search),是搜索算法的一种,它沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点v的所有边都已被探寻过,将回溯到发现节点v的那条边的起始节点。

Js中常见的几种循环遍历

项目开发中,不管是建立在哪个框架基础上,对数据的处理都是必须的,而处理数据离不开各种遍历循环。javascript中循环遍历有很多种方式,记录下几种常见的js循环遍历。

JavaScript中,数组和对象的遍历方法总结

循环遍历是写程序很频繁的操作,JavaScript 提供了很多方法来实现。这篇文章将分别总结数组和对象的遍历方法,新手可以通过本文串联起学过的知识。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!