TypeScript自定义事件监听器

5

我觉得在这里我对理解缺失了相当重要的东西,因此也许一点解释会非常有帮助。

我有一个类,如下所示:

class Entity {
    protected pos: number = [0,0];

    ...

    public setPos(x: number, y: number) {
        this.pos = [x, y];
    }
}

我希望您能够提供类似以下的东西

class EntityPositionListener {
    //do stuff when an Entity position changes
}

我还有Entity类的子类。 因此,每当Entity或其任何子类更改给定实例中的pos变量时,我希望能够了解并更新地图上的位置,以及捕获该实例,以便我可以在该实例上进行其他操作。

我注意到Firefox有Object.watch()命令,并且我在另一个问题中找到了一个“跨浏览器”的解决方案-但是没有用-我已经尝试在后期初始化阶段“包装”Entity类,但后者最终会覆盖Entity类或创建一个无限循环,所以显然我错过了某个点。如果包装是答案,那么我该如何在TypeScript中执行此操作?JS版本创建了一个循环或未起作用。

//This created an infinite loop in runtime, but more importantly overwrote the Entity class
function EntityListener(name) {
    cout(name);
}
var oldEntity = Entity;
function Entity(name) {
    if (typeof EntityListener === "function") {
        EntityListener(name);
    }

    oldEntity(name);
}

理想情况下,我想在一个给定的类中监听多个事物,比如在游戏更新循环中。这个特殊情况只是用于演示。我找到了无数链接,其中许多是 JavaScript 而不是明确的 TypeScript ,因此我真的希望有一个 TypeScript 解决方案和一些解释。我基本上不明白如何去做。
编辑
如果这是更好的问题版本,更简单地说,如何监听 Entity.setPos() 函数被调用?
2个回答

2
Object.observe” 很可能会被 废弃Object.watch 仅在 Gecko 中支持,因此除非你只针对 Firefox,否则不要在代码中使用它。
这意味着你必须编写自己的事件发射器。如果你喜欢,可以使用 TypeScript 的 getter 和 setter:
class Entity {
    private entitityListener:EntityPositionListener;
    private _pos:number[] = [];

    get pos():number[] {
        return this._pos;
    }
    set pos(value:number[]) {            
        this._pos = value;

        if (this.entitityListener) {
            this.entitityListener.reportEvent('pos', value);
        }
    }
}

class SuperEntity extends Entity {

}

let superEntity = new SuperEntity();
superEntity.pos = [1,2]; // this will call 'this.entitityListener.reportEvent()' and you don't need superEntity.setPos([1,2]);

这段话的意思是“将其转译为:”。
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Entity = (function () {
    function Entity() {
        this._pos = [];
    }
    Object.defineProperty(Entity.prototype, "pos", {
        get: function () {
            return this._pos;
        },
        set: function (value) {
            this._pos = value;
            if (this.entitityListener) {
                this.entitityListener.reportEvent('pos', value);
            }
        },
        enumerable: true,
        configurable: true
    });
    return Entity;
})();
var SuperEntity = (function (_super) {
    __extends(SuperEntity, _super);
    function SuperEntity() {
        _super.apply(this, arguments);
    }
    return SuperEntity;
})(Entity);
var superEntity = new SuperEntity();
superEntity.pos = [1, 2];

你可以使用 Object.defineProperty() 来定义 getter 和 setter,这意味着你可以定义 setter 包含对 EntityListener 方法的调用。

有没有可能更改这段代码,使EntityPositionListener在任何时候调用实体[set]时都会监听,而不修改setter函数? 我对这个解决方案的保留是,我将不得不返回并修改函数以激活侦听器中的功能,而不是让侦听器处理调用该函数的类的任何实例; 这是否可能? - Matt
我已经更新了答案。老实说,我认为你会找到一种方法来做你想做的事情。然而,在我看来,这样做迟早会招致反噬。我曾经也做过一些疯狂的事情,所以我是从自己的经验谈起的。 :-) - MartyIX
1
你是否接受使用TypeScript装饰器?https://dev59.com/vF0a5IYBdhLWcg3w88k0 可能是解决你问题的合理方案。 - MartyIX
不知道那些是什么,但它们乍一看很有趣。无论如何,我花了一点时间玩弄你的代码,我终于理解了这里的方法和你的警告,发现它比我最初想象的更实用。非常感谢你的帮助! :) - Matt
那些装饰器真的很棒!这基本上就是我所期望的 ^_^ - Matt

2
最好使用“subscribe”方法,这样您可以自动支持对象上的任意数量的事件监听器,而不仅仅是一个。并且使用强类型事件,以确保类型安全。
现在有几个库可以做到这一点。以下是通过sub-events实现的示例:
import {SubEvent} from 'sub-events';

type PosChanged = {newPos: number[], oldPos: number[]};

class Entity {

   // strongly-typed event:
   readonly onPosChanged: SubEvent<PosChanged> = new SubEvent();

   private _pos: number[] = [];

   get pos(): number[] {
       return this._pos;
   }

   set pos(newPos: number[]) {
       // can add verification here, if it actually changed;
       const oldPos = this._pos;
       this._pos = newPos;
       this.onPosChanged.emit({newPos, oldPos});
   }
}

任何派生类都可以订阅 pos 的变化:
const e = new Entity();

e.onPosChanged.subscribe(data => {
    // data is strongly-typed

    console.log(data); //-> { newPos: [ 1, 2, 3 ], oldPos: [] }
});

e.pos = [1, 2, 3]; // this will trigger the event

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接