JavaScript ES6:用全局空对象替代兜底新对象能否提升性能?
用全局单例空对象/数组替代临时创建的空值,确实能带来性能提升!
你的思路非常到位——每次写|| {}或者|| []的时候,其实都在创建一个全新的空对象/数组实例。如果这种代码出现在高频执行的路径里(比如循环、频繁触发的事件回调、或者被反复调用的工具函数),这些临时对象会不断占用内存,给垃圾回收(GC)带来额外压力,日积月累就会影响运行流畅度。
换成全局复用的单例空对象/数组,就能从根源上减少不必要的内存分配和GC开销,具体可以这么做:
定义冻结的全局单例
先在模块作用域(推荐,避免污染全局)或者全局作用域里定义这两个常量,并且用Object.freeze冻结它们:// 模块级的单例,不会污染全局命名空间 const EMPTY_OBJECT = Object.freeze({}); const EMPTY_ARRAY = Object.freeze([]);为什么要冻结?因为如果后续代码不小心修改了这个空对象(比如
someField.thing = 'test'),没冻结的话会直接污染这个全局单例,导致所有依赖它的地方都出现意料之外的属性,冻结之后修改操作会在严格模式下报错,非严格模式下静默失败,能保证单例始终是空的状态。替换原有代码
把原来的兜底逻辑换成单例即可:// 原来的写法 // const someField = (myObject.wantedKey || {}); // 替换后的写法 const someField = myObject.wantedKey || EMPTY_OBJECT; // 原来的数组兜底 // const nullIsempty = (myObject.undefinedIsEmpty || []); // 替换后的写法 const nullIsempty = myObject.undefinedIsEmpty || EMPTY_ARRAY;
关于性能提升的细节
- 如果是在单次执行的初始化代码里,这种优化几乎感知不到;
- 但如果是在循环体、高频事件(比如scroll/resize)、或者被频繁调用的函数里,累计的内存分配减少会很明显,GC的停顿次数会降低,整体运行更流畅。
额外的ES6+优化方案
如果你用的是ES2020及以上版本,可以结合**空值合并运算符??和可选链?.**来进一步简化逻辑,同时也能避免临时对象:
// 只针对null/undefined兜底,比||更精准(不会把0、''等假值替换掉) const someField = myObject.wantedKey ?? EMPTY_OBJECT; // 如果只是要访问嵌套属性,直接用可选链更简洁,返回undefined而非空对象 const thing = myObject.wantedKey?.thing;
最后再强调一遍:一定要冻结你的单例空对象/数组,否则很容易埋下难以排查的bug!
内容的提问来源于stack exchange,提问作者Howard Grimberg
相关产品推荐
相关产品推荐

