应避免在原型对象上挂载大体积动态数组,因其会导致内存不可回收、gc压力增大及共享副作用;推荐改用实例级惰性初始化、weakmap存储或模块顶层只读数据等方式解耦生命周期。

直接在原型对象上挂载大体积动态数组,本质是把本该有明确生命周期的数据,强行绑定到所有实例共享的静态结构上——它不污染数据内容,但会严重污染内存模型和 GC 行为。真正要防的不是“污染”,而是“不可回收”。
别让数组活在 prototype 上
像 MyClass.prototype.cacheList = new Array(100000) 这类写法,会让这个数组随构造函数长期驻留堆中。只要有一个实例没被回收,或构造函数被闭包捕获,整个原型链(含这个数组)就无法被标记为垃圾。
- 数组本身占用大量连续内存,易晋升至老年代,推高 Full GC 频率
- 所有实例共享同一引用,任意一处修改都影响全局,调试困难
- 单元测试时无法独立重置,容易产生跨用例副作用
改用实例级惰性初始化
把大数组从 prototype 拆出来,移到实例内部,并延迟到首次使用才创建:
- 构造函数里只设
this._cacheList = null或undefined - 用 getter 封装访问逻辑:
get cacheList() { return this._cacheList ?? (this._cacheList = new Array(100000)); } - 若需强隔离,可用
WeakMap存储:外部 map 映射实例 → 数组,避免强引用阻碍回收
全局只读数据请放模块顶层
如果这个数组确实是多处复用、不可变的(比如预计算的坐标索引表),就彻底剥离构造函数:
- 在模块文件顶部定义:
const GLOBAL_COORD_INDEX = new Float32Array(…); - 构造函数内仅引用它:
this.index = GLOBAL_COORD_INDEX; - 这样数据由模块加载器管理生命周期,不与任何实例耦合,也便于 mock 和热替换
必须共享时,做轻量访问封装
若业务逻辑强依赖“所有实例共用同一份可变数组”,不要直接挂数组,而是封装访问层:
- 原型上只放小函数:
MyClass.prototype.findInIndex = function(key) { return SharedIndex.get(key); }; - 真实数组放在外部单例或私有静态字段(ES2022+ 可用
#index) - 提供明确的销毁接口:
SharedIndex.clear(),方便测试清理或运行时重置
关键不是“能不能挂”,而是“谁来决定它什么时候该消失”。把大数组的生命周期从 prototype 解耦出来,GC 才能真正看清哪些是垃圾。











