网上讲 JavaScript 性能优化的文章,绝大多数只给结论不给数据。我把 8 个最常见的优化手法在 Node 上真跑了一遍,结果有点反直觉:8 个里面有 3 个"经典优化"其实更慢。
先说真的快的三个
1. 按 id 查找:Map 代替 find —— 实测 680 倍
// 慢:每次都要遍历constuser=list.find(u=>u.id===targetId);// 快:一次建索引,之后 O(1)constmap=newMap(list.map(u=>[u.id,u]));constuser=map.get(targetId);数据量越大差距越夸张。20 万条数据的场景下,这一条改动往往就能让页面从"卡"变成"不卡"。
2. 数组去重:Set 代替双重循环 —— 实测 90 倍
把out.indexOf(v)写在循环里就是 O(n²),而[...new Set(arr)]是 O(n)。
3. 记忆化递归 —— 实测 800 倍
斐波那契这类"重叠子问题",加一层缓存就是降维打击:
constmemo=newMap();constfib=(n)=>{if(n<2)returnn;if(memo.has(n))returnmemo.get(n);constv=fib(n-1)+fib(n-2);memo.set(n,v);returnv;};反直觉:这三个"优化"其实更慢
4. 深拷贝:小对象上 structuredClone 比 JSON 慢 2.3 倍
很多人现在无脑推荐structuredClone。实测下来,对纯 JSON 数据它反而更慢——因为它要做完整的结构化克隆与类型检查。它的价值在于正确性(能克隆 Date、Map、Set、ArrayBuffer、循环引用),而不是速度。
结论:纯数据用JSON.parse(JSON.stringify());含特殊类型才用structuredClone。
5. 字符串拼接:现代 V8 下+=与join几乎没差别
实测 0.9 倍(join还略慢)。V8 早就对字符串拼接做了优化,"拼接要用数组 join"这条老经验已经不成立了。可读性优先。
6. 条件分支改写成查表:反而慢
只有当分支足够多、键取值范围可控时查表才有优势。分支很少时,多出来的对象查找开销比分支判断还贵。
为什么会"越优化越慢"
- 引擎已经优化过了:十年前的经验未必适用于今天的 V8;
- 前提条件不成立:很多优化只在特定数据规模下成立;
- 优化引入新开销:多一次建索引、多一次类型检查,小数据量下就是净亏损。
正确的工作流(比任何技巧都重要)
- 先测量,再优化:没有 profile 的优化都是猜;
- 先记基线:耗时、内存、包体积,改之前先写下来;
- 一次只改一个变量:否则不知道是哪一步起了作用;
- 算收益比:90 倍的改动值得做,0.9 倍的改动不值得。
怎么复现这些数字
只要装个 Node(18+),把基准脚本跑一遍就行:
functiontimeit(fn,times){constt0=process.hrtime.bigint();for(leti=0;i<times;i++)fn();returnNumber(process.hrtime.bigint()-t0)/1e6;}注意两点:先预热再计时(避免首次 JIT 影响结论)、同样条件重复多轮。
我把这 8 项优化整理成了一套可直接运行的实战包:包含 before/after 基准脚本(能改成测你自己的代码)、Node 内置测试器写的单元测试示例、以及 ESLint / Vite / Webpack 的工程化配置模板与逐项说明。在 CSDN 下载里搜索「JavaScript性能优化实战」即可找到。
基准数字来自本机实跑,CPU 与 Node 版本不同会有差异,但趋势一致。
文中那 8 个基准测试的完整代码、工程化配置、优化清单 30 条,我打包好了(可直接跑):
https://download.csdn.net/download/2604_96203225/93505927