在前端开发中,循环是必不可少的工具。无论是处理数组数据还是操作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 (let i = 0; i < array.length; i++) {
// 每次循环都需要:
// 1. 检查数组长度(可能变化)
// 2. 检查索引边界
// 3. 检查数组类型
result += array[i];
}
// 对比map
array.map(item => item * 2);
// 引擎知道这是纯转换操作,可以大胆优化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}`
}));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;
});console.time('filter');
const evens = testArray.filter(item => item % 2 === 0);
console.timeEnd('filter');性能:约1.44ms
特色:
专为过滤设计,语义清晰
性能优秀
返回新数组
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;
}, {});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];
}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、内存压力变化等都会影响结果。
// 推荐使用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);// 只能使用for循环
function findUserById(users, id) {
for (let i = 0; i < users.length; i++) {
if (users[i].id === id) {
return users[i]; // 找到立即返回
}
}
return null;
}// 使用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 });// 推荐forEach或for...of
document.querySelectorAll('.button')
.forEach(button => {
button.addEventListener('click', handleClick);
});
// 或者
for (const button of document.querySelectorAll('.button')) {
button.addEventListener('click', handleClick);
}// 优化版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;
}// 避免多次循环
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));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;
}// 类型不一致,影响优化
const array = [1, 2, '3', 4]; // 混合类型
// 类型一致,便于优化
const array = [1, 2, 3, 4]; // 单一类型| 场景 | 推荐方法 | 理由 |
|---|---|---|
| 数据转换 | map | 性能最佳,语义明确 |
| 数据过滤 | filter | 专为过滤设计 |
| 数据聚合 | reduce | 表达能力最强 |
| 需要提前退出 | for循环 | 唯一支持break的方法 |
| 遍历可迭代对象 | for...of | 最通用的遍历方式 |
| 简单遍历 | forEach | 平衡性能和可读性 |
| 极致性能要求 | 优化for循环 | 手动优化缓存长度 |
默认选择map/filter/reduce - 在大多数情况下性能更好,代码更清晰
需要提前退出时用for循环 - 这是它的独特优势
处理可迭代对象用for...of - 最通用的遍历方式
保持类型一致性 - 帮助引擎更好优化
避免不必要的函数创建 - 减少内存分配
写出既容易理解又能被引擎优化的代码,才是真正优秀的代码。根据具体场景选择合适的方法,不要盲目追求性能而牺牲代码的可读性。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
循环是所有语言最基础的语法。这篇文章将介绍avascript多种循环遍历的实现介绍,包括for ..in遍历方式 ,do..while,forEach,for…of,map等等,
JavaScript数组遍历的区别,推荐在循环对象属性的时候使用for in,在遍历数组的时候的时候使用for of;for in循环出的是key,for of循环出的是value;for of是ES6新引入的特性。修复了ES5的for in的不足;
遍历二叉树,这个相对比较复杂。二叉树的便利,主要有两种,一种是广度优先遍历,一种是深度优先遍历。什么是广度优先遍历?就是根节点进入,水平一行一行的便利。
数组遍历:for --使用变量将数组长度缓存起来,在数组较长时性能优化效果明显,forEach --ES5语法;对象遍历:for...in --以任意顺序遍历一个对象自有的、继承的、可枚举的
二叉树的顺序存储结构就是用一维数组存储二叉树中的各个结点,并且结点的存储位置能体现结点之间的逻辑关系。二叉树的遍历有三种方式,如下:
相同点:都是循环遍历数组中的每一项;每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组)
for in语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作);For循环可以将代码块执行指定的次数。forEach按照原始数组元素顺序依次处理元素.
深度优先遍历(Depth-First-Search),是搜索算法的一种,它沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点v的所有边都已被探寻过,将回溯到发现节点v的那条边的起始节点。
项目开发中,不管是建立在哪个框架基础上,对数据的处理都是必须的,而处理数据离不开各种遍历循环。javascript中循环遍历有很多种方式,记录下几种常见的js循环遍历。
循环遍历是写程序很频繁的操作,JavaScript 提供了很多方法来实现。这篇文章将分别总结数组和对象的遍历方法,新手可以通过本文串联起学过的知识。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!