JS执行速度优化
这是关于JavaScript刷算法题提高执行速度、降低执行时间的技巧分享
在 JavaScript 中刷力扣算法题时,执行时间受代码细节、数据结构选择和语言特性影响较大。以下是经过实践验证的 10 个降低执行时间的核心技巧,结合具体场景说明:
1. 用 for 循环替代数组方法(forEach/map/filter)
- 原因:
forEach等方法本质是函数调用,每次迭代会创建函数作用域,存在额外开销;for循环是原生语法,执行效率更高。 - 场景:大规模数组遍历(如长度 > 10000)。
- 示例:javascript
// 较慢 let sum = 0; arr.forEach(num => sum += num); // 更快 let sum = 0; for (let i = 0; i < arr.length; i++) { sum += arr[i]; }
对比for...of:
传统 for 循环是最基础的迭代方式,通过索引直接访问元素(如 nums[i]),逻辑简单,JavaScript 引擎(如 V8)对其优化程度最高(可进行循环展开、边界检查消除等底层优化)。for...of 循环基于迭代器协议,需要创建迭代器对象、调用 next() 方法、检查 done 属性等额外操作,这些会产生微小的性能开销,累积后在大数据量场景中会拉开差距。
2. 缓存数组长度,避免重复计算
- 原因:
for循环中每次判断i < arr.length时,都会重新计算数组长度(尤其对于动态数组),缓存后可减少操作。 - 场景:任何
for循环遍历。 - 示例:javascript
// 较慢 for (let i = 0; i < arr.length; i++) { ... } // 更快 for (let i = 0, len = arr.length; i < len; i++) { ... }
3. 优先使用 Map/Set 优化查找效率
- 原因:数组的
indexOf/includes是 O(n) 复杂度,而Map的has或Set的has是 O(1)。 - 场景:需要频繁判断“元素是否存在”(如两数之和、去重)。
- 示例:javascript
// 较慢(O(n²)) function findTarget(arr, target) { for (let i = 0; i < arr.length; i++) { if (arr.includes(target - arr[i]) && ...) { ... } } } // 更快(O(n)) function findTarget(arr, target) { const map = new Map(); for (let i = 0; i < arr.length; i++) { if (map.has(target - arr[i])) { ... } map.set(arr[i], i); } }
4. 避免不必要的变量复制和引用类型操作
- 原因:对象/数组的复制(如
slice、扩展运算符...)会创建新内存,耗时随数据量增长。 - 场景:原地修改数组(如排序、覆盖元素)。
- 示例:javascript
// 较慢(复制数组) const newArr = arr.slice(); newArr.sort((a, b) => a - b); // 更快(原地排序) arr.sort((a, b) => a - b); // 直接修改原数组
5. 用 while 循环处理高频迭代场景
- 原因:
while循环的语法解析开销略低于for循环,在极端高频迭代(如百万级)时更优。 - 场景:数值计算(如斐波那契、阶乘)、链表遍历。
- 示例:javascript
// 计算 n 的阶乘(while 比 for 略快) function factorial(n) { let res = 1; while (n > 1) { res *= n; n--; } return res; }
6. 减少全局变量访问,优先使用局部变量
- 原因:JavaScript 查找变量时,全局作用域比局部作用域层级更深,访问耗时更长。
- 场景:函数内频繁使用的变量(如循环计数器、中间结果)。
- 示例:javascript
// 较慢(全局变量) let globalSum = 0; function sumArr(arr) { for (let i = 0; i < arr.length; i++) { globalSum += arr[i]; // 每次访问全局变量 } } // 更快(局部变量) function sumArr(arr) { let localSum = 0; // 局部变量 for (let i = 0; i < arr.length; i++) { localSum += arr[i]; } return localSum; }
7. 利用位运算替代数学运算
- 原因:位运算直接操作二进制,比算术运算(如乘除、取模)更快。
- 场景:整数除以 2(
n >> 1)、判断奇偶(n & 1)、取模 2^k 等。 - 示例:javascript
// 较慢 const half = n / 2; const isOdd = n % 2 === 1; // 更快 const half = n >> 1; // 等价于 Math.floor(n / 2) const isOdd = (n & 1) === 1;
8. 避免字符串拼接的低效操作
- 原因:字符串在 JavaScript 中是不可变的,每次
+拼接都会创建新字符串,复杂度 O(n²)。 - 场景:大量字符串拼接(如拼接数组元素)。
- 优化方案:用
Array.join('')替代+,因为数组可以原地修改,最后一次性拼接。javascript// 较慢 let str = ''; for (let i = 0; i < 10000; i++) { str += i; // 每次创建新字符串 } // 更快 const arr = []; for (let i = 0; i < 10000; i++) { arr.push(i); } const str = arr.join(''); // 一次拼接
9. 提前处理边界条件,减少无效计算
- 原因:在循环或递归前判断边界(如空数组、0/1 等特殊值),可避免不必要的执行。
- 场景:数组/链表操作、递归函数(如斐波那契、二叉树遍历)。
- 示例:javascript
// 较慢(进入循环后才判断) function sumArr(arr) { let sum = 0; for (let i = 0; i < arr.length; i++) { sum += arr[i]; } return sum; } // 更快(提前退出) function sumArr(arr) { if (arr.length === 0) return 0; // 边界条件优先 let sum = 0; for (let i = 0, len = arr.length; i < len; i++) { sum += arr[i]; } return sum; }
10. 用 typedArray 处理大量数值数据
- 原因:普通数组可以存储任意类型,而
TypedArray(如Uint32Array)仅存储特定类型的数值,内存占用更少,访问速度更快。 - 场景:图像处理、大规模数值计算(如前缀和、滑动窗口)。
- 示例:javascript
// 处理大量整数时,TypedArray 比普通数组快 const nums = new Uint32Array(1000000); // 仅存 32 位无符号整数 for (let i = 0; i < nums.length; i++) { nums[i] = i; // 赋值和访问更快 }
总结
JavaScript 优化的核心是 “减少不必要的开销”:
- 用高效语法(
for/while替代函数式方法); - 选对数据结构(
Map/Set优化查找); - 避免冗余操作(全局变量、重复复制)。
实际刷题时,先保证算法复杂度最优(如 O(n) 优于 O(n²)),再用上述技巧优化常数时间,执行效率会显著提升。