你是不是也遇到过这种情况:页面数据一多,滚动起来就卡成PPT?明明代码写得没问题,但用户总抱怨应用太慢?
很多时候,问题就出在最基础的循环上!今天我就带你彻底搞懂JavaScript中的各种循环,帮你避开那些看不见的性能大坑。
读完本文,你会掌握各种循环的适用场景,学会如何选择最高效的遍历方式,让你的应用性能瞬间提升一个档次!
先来看看最熟悉的for循环
for循环是我们最早接触的循环方式,也是最基础、最灵活的。
// 最传统的for循环
for (let i = 0; i < array.length; i++) {
console.log(array[i]); // 逐个打印数组元素
}
这里有个小细节要注意:每次循环都会读取array.length,如果数组长度不变,最好先缓存起来。
// 优化版:缓存数组长度
for (let i = 0, len = array.length; i < len; i++) {
console.log(array[i]); // 性能更好一些
}
for循环最大的优点就是可控性强,你可以随时用break跳出或者continue跳过当前循环。
while和do-while怎么选?
while是先判断再执行,do-while是先执行再判断。
// while循环:可能一次都不执行
let i = 0;
while (i < 5) {
console.log(i); // 打印0到4
i++;
}
// do-while循环:至少执行一次
let j = 0;
do {
console.log(j); // 即使条件不成立也会执行一次
j++;
} while (j < 5);
do-while适合那些至少要执行一次的场景,比如读取用户输入直到正确为止。
数组遍历的现代方式:forEach
forEach是数组特有的方法,写起来更简洁。
const fruits = ['苹果', '香蕉', '橙子'];
fruits.forEach(function(fruit, index) {
console.log(index + ': ' + fruit); // 输出"0: 苹果"等
});
用箭头函数写更简洁:
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`); // 使用模板字符串
});
但是注意:forEach不能用break中断循环,也不能用continue跳过当前项。如果需要中途退出,还得用传统的for循环。
for…of循环:ES6的新选择
for…of是ES6引入的,可以遍历任何可迭代对象。
// 遍历数组
const numbers = [1, 2, 3];
for (const num of numbers) {
console.log(num); // 1, 2, 3
}
// 遍历字符串
const str = 'hello';
for (const char of str) {
console.log(char); // 'h', 'e', 'l', 'l', 'o'
}
for…of的优点是可以直接获取值,不需要通过索引,而且支持break和continue。
性能大比拼:谁才是真正的速度之王?
说了这么多,到底哪种循环最快呢?我们来做个测试。
先创建一个包含100万个元素的数组:
const bigArray = new Array(1000000).fill().map(() => Math.random());
然后分别测试各种循环的耗时:
// 测试for循环
console.time('for循环');
for (let i = 0, len = bigArray.length; i < len; i++) {
// 什么都不做,只是遍历
}
console.timeEnd('for循环');
// 测试forEach
console.time('forEach');
bigArray.forEach(item => {
// 什么都不做
});
console.timeEnd('forEach');
// 测试for...of
console.time('for...of');
for (const item of bigArray) {
// 什么都不做
}
console.timeEnd('for...of');
在我的电脑上测试结果是这样的:
- for循环:约5ms
- forEach:约15ms
- for…of:约30ms
传统for循环完胜!forEach大概是for循环的3倍时间,for…of则是6倍左右。
为什么性能差这么多?
其实原因很简单:
- for循环是最底层的实现,直接通过索引访问,没有额外开销
- forEach是函数调用,每次迭代都要执行一次回调函数,有函数调用的开销
- for…of需要创建一个迭代器对象,也有额外的开销
但是!先别急着把所有循环都改成for循环。
实际开发中该怎么选?
性能测试只是理论值,实际开发中还要考虑代码可读性和维护性。
什么时候用for循环:
- 需要高性能遍历大数据量时
- 需要中途break或continue时
- 需要同时操作多个数组时
什么时候用forEach:
- 代码简洁性比极致性能更重要时
- 不需要中断循环时
- 链式调用数组方法时
什么时候用for…of:
- 遍历非数组的可迭代对象时(如Set、Map)
- 需要break/continue但又想写简洁代码时
简单来说就是:
- 要性能选for
- 要简洁选forEach
- 要通用选for…of
一些实用的小技巧
用break提前退出循环:
for (let i = 0; i < array.length; i++) {
if (array[i] === '目标值') {
console.log('找到了!');
break; // 找到后就退出循环
}
}
用continue跳过本次循环:
for (let i = 0; i < array.length; i++) {
if (array[i] % 2 === 0) {
continue; // 跳过偶数
}
console.log(array[i]); // 只打印奇数
}
循环嵌套的性能优化:
// 不好的写法:内层循环每次都要计算长度
for (let i = 0; i < outerArray.length; i++) {
for (let j = 0; j < innerArray.length; j++) {
// 每次都要读取innerArray.length
}
}
// 优化写法:缓存内层数组长度
for (let i = 0, outerLen = outerArray.length; i < outerLen; i++) {
for (let j = 0, innerLen = innerArray.length; j < innerLen; j++) {
// 性能更好
}
}
总结一下
今天我们把JavaScript中的循环彻底讲透了。记住这几个要点:
- for循环性能最好,适合处理大数据量
- forEach写起来最简洁,但性能稍差
- for…of最通用,但性能最差
- 实际开发中要在性能和可读性之间权衡
不要盲目追求性能而牺牲代码可读性,除非你确实在处理海量数据。大多数情况下,代码的可维护性比那几毫秒的性能提升更重要。
对了,你平时最喜欢用哪种循环?有没有因为循环性能问题踩过坑?欢迎在评论区分享你的经历!
原文链接:https://mp.weixin.qq.com/s/AYapL2QYJXdcIPBzVgybMA