You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为第三方模块的DisplayObject子类实例添加独立的updatables集合?

问题:扩展PIXI.js DisplayObject时实例共享updatables集合的优化方案

我尝试扩展第三方模块pixi.js中的DisplayObject类,通过TypeScript接口合并为其添加updatables(Set<Updatable>类型)属性与update方法,并通过原型注入实现相关逻辑:

declare module 'pixi.js' {
    interface DisplayObject {
        updatables: Set<Updatable>
        update(delta: number): void
    }
}

DisplayObject.prototype.updatables = new Set()

DisplayObject.prototype.update = function (delta: number) {
    this.updatables.forEach(u => u.update(delta))
    // ...
}

该实现成功为所有DisplayObject及其子类添加了update方法,但updatables集合为所有实例共享,无法实现每个实例拥有独立的Set。目前我通过懒加载get/set实现了需求,但感觉该方案略显繁琐且不够优雅,想寻求更合适的实现方式。

补充说明:众多第三方模块类继承自DisplayObject,TypeScript接口合并已正确更新所有子类的接口,update方法也已通过原型成功注入,但updatables集合为实例共享而非每个实例独立。


解决方案1:使用Symbol作为属性键+懒初始化

利用Symbol的唯一性,在原型的update方法中为每个实例初始化独立的updatables集合,避免原型共享问题:

// 定义唯一Symbol键,避免属性名冲突
const UPDATABLES_KEY = Symbol('updatables');

declare module 'pixi.js' {
    interface DisplayObject {
        [UPDATABLES_KEY]: Set<Updatable>
        update(delta: number): void
    }
}

// 原型注入update方法
DisplayObject.prototype.update = function (delta: number) {
    // 懒初始化当前实例的updatables集合
    if (!this[UPDATABLES_KEY]) {
        this[UPDATABLES_KEY] = new Set();
    }
    this[UPDATABLES_KEY].forEach(u => u.update(delta));
    // ...其他逻辑
};

// 可选:添加getter保持原有的this.updatables访问方式
Object.defineProperty(DisplayObject.prototype, 'updatables', {
    get() {
        if (!this[UPDATABLES_KEY]) {
            this[UPDATABLES_KEY] = new Set();
        }
        return this[UPDATABLES_KEY];
    },
    enumerable: true,
    configurable: true
});

这种方式既保证了每个实例的updatables独立,又通过Symbol避免了属性名冲突,同时可选的getter可以兼容原有的访问习惯。

解决方案2:用WeakMap存储实例与updatables的映射

使用WeakMap来存储每个实例对应的updatables集合,WeakMap的键是弱引用,不会影响实例的垃圾回收,避免内存泄漏:

const instanceUpdatables = new WeakMap<DisplayObject, Set<Updatable>>();

declare module 'pixi.js' {
    interface DisplayObject {
        updatables: Set<Updatable>
        update(delta: number): void
    }
}

DisplayObject.prototype.update = function (delta: number) {
    let updatables = instanceUpdatables.get(this);
    if (!updatables) {
        updatables = new Set();
        instanceUpdatables.set(this, updatables);
    }
    updatables.forEach(u => u.update(delta));
    // ...其他逻辑
};

// 添加getter/setter支持直接访问updatables
Object.defineProperty(DisplayObject.prototype, 'updatables', {
    get() {
        let updatables = instanceUpdatables.get(this);
        if (!updatables) {
            updatables = new Set();
            instanceUpdatables.set(this, updatables);
        }
        return updatables;
    },
    set(newSet: Set<Updatable>) {
        instanceUpdatables.set(this, newSet);
    },
    enumerable: true,
    configurable: true
});

这种方式完全不污染实例的属性空间,所有状态都存放在外部的WeakMap中,适合担心属性冲突的场景。

解决方案3:包装DisplayObject构造函数

通过包装原构造函数,在实例创建时为其添加独立的updatables属性:

const OriginalDisplayObject = DisplayObject;

// 重写构造函数
DisplayObject = function(...args: any[]) {
    const instance = new OriginalDisplayObject(...args);
    // 为当前实例添加独立的updatables
    instance.updatables = new Set<Updatable>();
    return instance;
} as typeof OriginalDisplayObject;

// 继承原原型链
DisplayObject.prototype = OriginalDisplayObject.prototype;
DisplayObject.prototype.constructor = DisplayObject;

// 注入update方法
DisplayObject.prototype.update = function (delta: number) {
    this.updatables.forEach(u => u.update(delta));
    // ...其他逻辑
};

// TypeScript接口合并保持不变
declare module 'pixi.js' {
    interface DisplayObject {
        updatables: Set<Updatable>
        update(delta: number): void
    }
}

注意:如果有子类直接调用super(),这种方式依然能正常工作,子类实例也会自动获得独立的updatables集合。


内容的提问来源于stack exchange,提问作者UXK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:34:53