函数作用域内应避免过度创建对象,核心是复用、延迟创建和按需分配;高开销对象如regexp、intl.numberformat等需提升至模块级缓存;小对象在高频场景下也应复用并原地修改;闭包使用需谨慎,避免隐式对象生成;对象池和静态工厂适用于短生命周期对象管理。

函数作用域内避免过度创建对象,核心不是“少写 new”,而是让对象复用、延迟创建、按需分配。很多性能问题其实来自无意识的重复构造——比如每次调用都新建正则、数组、配置对象或临时结构体。
把高开销对象提到函数外缓存
像 RegExp、Intl.NumberFormat、大型 Map 或带复杂逻辑的工具实例,创建成本高,且本身不可变或状态可重置。应声明为函数外部的常量或模块级变量。
- 正则表达式务必预编译:用
const PATTERN = /.../g或RegExp.compile(...),别在函数里反复调用new RegExp()或字符串.match() - 格式化器、解析器等重对象(如
JSON.parse的替代方案、日期格式器)适合单例封装,而非每次调用都初始化 - 注意:缓存对象必须是线程安全(前端即“调用安全”)的;若需不同配置,可用工厂函数生成有限个实例,而不是每次都 new
复用已有对象,而非每次都 new
小对象(如 {x: 0, y: 0})创建开销虽小,但在高频循环或动画帧中累积起来仍会触发频繁 GC。优先复用、原地修改。
- 提前声明可复用的对象或数组:例如
const tempVec = {x: 0, y: 0};,在函数内只改属性值,不重新赋值 - 数组尽量复用
.length = 0清空,比[]创建新数组更轻量 - 对简单数据结构(坐标、颜色、矩形),考虑用扁平数组代替对象:
[x, y]比{x, y}内存更紧凑、访问更快
用参数替代闭包捕获带来的隐式对象生成
在循环或回调中,靠闭包“记住”变量容易导致每个迭代都生成独立闭包,间接创建多余对象(尤其捕获整个 event、response 或大数组)。
- 循环中用
let声明变量,避免var引发的闭包共享问题 - 把需要的数据显式传入回调,而不是让它从外层作用域闭包中读取:
button.addEventListener('click', () => handler(id, data))→ 改为button.addEventListener('click', handler.bind(null, id, data))或直接传参 - 避免闭包长期持有 DOM 节点、
File、Blob等大对象;只需字段时,提前解构并缓存轻量值(如el.id、file.name)
合理使用对象池和静态工厂
对于必须动态创建但生命周期短、结构固定的对象(如粒子、事件对象、请求上下文),对象池比频繁 new 更可控。
- 简单池可基于数组实现:
acquire()取出或新建,release(obj)归还;关键是要统一管理生命周期 - 静态工厂方法适合封装构造逻辑:比如
Point.create(x, y)内部复用池或返回缓存实例,对外隐藏细节 - 注意池的大小限制和清理策略,防止内存长期占用;不用时及时
pool.length = 0或清空引用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











