应缓存 DOM 查询结果、按场景选用节流或防抖、用 requestIdleCallback 处理低优先级任务、避免使用 with/eval。这些优化可显著提升渲染性能和响应速度。

避免在循环中重复计算 DOM 查询
每次调用 document.getElementById 或 querySelector 都会触发 浏览器 的样式计算和布局查找,频繁调用会显著拖慢渲染。尤其在 for 或 forEach 中反复查同一个元素,纯属浪费。
- 把查询结果缓存到变量里,后续直接复用
- 如果要批量操作多个同类节点,优先用
querySelectorAll一次获取 NodeList,再遍历 —— 它比多次单查快得多 - 注意:NodeList 是静态快照,若后续 DOM 动态增删,它不会自动更新
const container = document.getElementById('list'); // ✅ 缓存一次 const items = container.querySelectorAll('.item'); // ✅ 批量查 items.forEach(item => {item.classList.add('processed'); });
节流与防抖别混用,看场景选对函数
滚动、输入、窗口缩放等高频事件不加控制,会瞬间堆积几十上百次回调,直接卡死主线程。但节流(throttle)和防抖(debounce)解决的是两类问题,错用反而坏事。
- 用户持续滚动 / 拖拽时,只需「固定频率执行」—— 用
throttle(如每 100ms 最多执行一次) - 用户输入搜索词,希望等他停手后再请求 —— 用
debounce(如停 300ms 后才触发) - 别在
resize里用防抖:窗口拉伸过程可能被完全忽略,导致布局错乱
function throttle(fn, delay) {let last = 0; return (……args) => {const now = Date.now(); if (now - last> delay) {fn(……args); last = now; } }; }
用 requestIdleCallback 处理低优先级任务
不是所有逻辑都得立刻执行。比如日志上报、非关键动画帧、预加载下一页数据 —— 这些塞进主任务队列,会挤占用户交互响应时间。Chrome 和 Safari 支持的 requestIdleCallback 能帮你把它们“排队”到浏览器空闲时段运行。
- 它接收一个 回调函数 和可选的
timeout(毫秒),超时后强制执行,防止任务被无限搁置 - 注意:Firefox 不支持,需用
setTimeout降级;且回调中不能做 DOM 修改(会触发重排) - 适合做「锦上添花」类操作,别依赖它完成核心功能
if ('requestIdleCallback' in window) {requestIdleCallback(() => {console.log('现在空闲,可以发埋点'); }, {timeout: 2000}); } else {setTimeout(() => console.log('降级处理'), 0); }
对象属性访问别滥用 with 和 eval
这两个语法会让 JS 引擎无法做 JIT 优化,直接退化成解释执行,性能暴跌。现代代码几乎没理由用它们,但有些老库或动态模板拼接仍残留这类写法。
立即学习“Java 免费学习笔记(深入)”;
-
with会污染作用域链,引擎无法静态分析变量归属,所有属性访问都变慢 -
eval执行字符串代码,必须重新解析 + 编译,还可能意外覆盖局部变量 - V8 的 TurboFan 编译器遇到它们会直接放弃优化整个函数
替代方案:用 Object.keys/Object.entries 遍历;动态属性名用方括号 obj[key];配置驱动逻辑用 switch 或查表对象代替 eval 拼函数名。
真实项目里最常被忽略的,是把「能延迟」和「必须同步」的任务混在一起调度 —— 比如在点击回调里既更新 UI 又发三路埋点又预加载资源。拆开、分级、交由不同机制(微任务 / 空闲回调 / Web Worker)处理,比单纯压缩代码更能稳住帧率。






























