aipooljs — 物件池
import { Aside } from ‘@astrojs/starlight/components’;
aipooljs 解決的問題很具體:遊戲熱路徑(子彈、粒子、特效)每幀都在大量建立與銷毀相同形狀的物件,讓 GC 不斷追著跑。object pool 用一個固定大小的預配置緩衝區取代這些配置,讓 acquire() / release() 都是 O(1),不產生 GC 壓力。
pnpm add aipooljsimport { createPool } from "aipooljs";
// 1. 預配置固定大小的緩衝區const bullets = createPool({ create: () => ({ x: 0, y: 0, vx: 0, vy: 0, active: false }), reset: (b) => { // reset 必須用賦值,不要用 delete(會讓 V8 降格 hidden class) b.x = 0; b.y = 0; b.vx = 0; b.vy = 0; b.active = false; }, size: 256,});
// 2. 熱路徑:acquire — O(1),不配置function fireBullet(x: number, y: number, vx: number, vy: number) { const b = bullets.acquire(); b.x = x; b.y = y; b.vx = vx; b.vy = vy; b.active = true; return b;}
// 3. 子彈消失時 release — O(1),reset() 先跑再歸還function recycleBullet(b: ReturnType<typeof fireBullet>) { bullets.release(b);}關鍵設計決策
Section titled “關鍵設計決策”固定大小,溢出即丟錯:acquire() 在池空時會丟出 PoolError,強迫你調整上游產生速率,而不是讓池暗自擴張讓最壞情況難以預測。若確實需要彈性,可傳入 onOverflow: 'grow'。
雙重釋放偵測:release() 同一個物件兩次是 object pool 的經典 bug,aipooljs 用 Set 追蹤存活物件,第二次 release 直接丟出 PoolError。
reset 不用 delete:刪除屬性會讓 V8 將物件降格成 dictionary mode,破壞 hidden class 最佳化,讓整個熱路徑從 monomorphic 變成 megamorphic。JSDoc 有明確標注這個規則。
createPool({ create, // () => T — 初始建立物件 reset, // (obj: T) => void — 歸還時重設 size, // number — 池子大小(必填) onOverflow, // 'throw'(預設)| 'grow' | (pool) => T})- 子彈/粒子池:最典型的用法,搭配
aiecsjs的 entity 管理 - 與
aiquadtreejs搭配:pool 管理物件生命週期,quadtree 管理空間查詢 - DOM 節點回收:
<li>列表項目、畫布繪圖物件等可以用同樣模式管理