TypeScript 自定义事件监听器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33577384/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:07:21  来源:igfitidea点击:

TypeScript custom event listener

typescript

提问by Matt

I think I'm missing something quite substantial from the understanding here, so perhaps a little explanation, too, would be incredibly helpful.

我想我在这里的理解中遗漏了一些非常重要的东西,所以也许一些解释也会非常有帮助。

I have a class, as such:

我有一堂课,例如:

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

    ...

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

and would like something like

并想要类似的东西

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

I also have descendants of the Entityclass. As such, any time any descendent of Entityor Entityitself changes the posvariable (in a given instance), I would like to know about it so that I can update the position on the map, as well as capture the instance so I could theoretically do other things to that instance.

我也有Entity班级的后代。因此,任何时候任何后代EntityEntity它本身更改pos变量(在给定实例中),我想知道它,以便我可以更新地图上的位置,以及捕获实例,以便理论上我可以做其他事情到那个实例。

I've noticed that Firefox has that Object.watch()command and I found a "cross-browser" solution someone had posted in another question--to no avail--and I've tried "wrapping" the Entityclass in a post-init phase, but the latter just ends up overwriting the Entityclass or creates a infinite loop, so I'm obviously missing the point somewhere. If wrapping is the answer, how do I do this in TypeScript? The JS version created a loop or didn't work.

我注意到 Firefox 具有该Object.watch()命令,并且我发现了某人在另一个问题中发布的“跨浏览器”解决方案 - 无济于事 - 我尝试Entity在 post-init 阶段“包装”该类,但是后者最终会覆盖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);
}

Ideally, I'd like to listen to several things on a given class--such as in the game update loop--this particular case just used for demonstration. I've found countless links, many of which are JS and not explicitly TypeScript, so I would really like a TypeScript solution and some explanation as to what's going on. I am just fundamentally not understanding how to go about this.

理想情况下,我想听几个给定类的东西——比如在游戏更新循环中——这个特殊的例子只是用于演示。我发现了无数的链接,其中许多是 JS 而不是明确的 TypeScript,所以我真的很想要一个 TypeScript 解决方案和一些关于正在发生的事情的解释。我只是从根本上不明白如何去做。

EDIT

编辑

If this is a better version of the question, maybe more simply, how do I listen for Entity.setPos()to be called?

如果这是问题的更好版本,也许更简单,我该如何聆听Entity.setPos()被调用?

采纳答案by Martin Vseticka

Object.observewill be most likely dropped. Object.watchis only supported in Gecko, so don't use it in your code unless you target only Firefox.

Object.observe很可能会被删除Object.watch仅在 Gecko 中受支持,因此除非您仅针对 Firefox,否则不要在您的代码中使用它。

This means that you have to write your own event emitting. You can use TypeScript getters and setters if you like them:

这意味着您必须编写自己的事件发射。如果你喜欢,你可以使用 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]);

which transpiles to:

转换为:

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];

You can use Object.defineProperty()to define getters and setters and this means that you can define setter to contain a call to a EntityListenermethod.

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

回答by vitaly-t

Best is to use the subscribeapproach, so you can automatically support any number of event listeners on the object, and not just one. And use a strongly-typed event, for type safety.

最好是使用该subscribe方法,这样您就可以自动支持对象上任意数量的事件侦听器,而不仅仅是一个。并使用强类型事件,以确保类型安全。

Several libraries can do such a thing today. Here's an implementation via 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});
   }
}

Any derived class then can subscribe to the poschange:

任何派生类都可以订阅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