观察者模式,利用TypeScript简单重构Event

本文介绍如何使用TypeScript重构Event类,实现自定义事件抛发与侦听机制,通过创建EmitterEvent和EmitterTarget类,实现数据随事件传递及事件监听。

观察者模式,利用TypeScript简单重构Event

如果想给一个Box类的实例化对象添加事件侦听,正常来说,我们写一个Box类:

Box.ts

export default class Box extends EventTarget{
    //给Box设置一个静态只读字符串类型的事件类型变量
    static readonly EVENT_ID:string = "Box_event_id";
    //给一个私有属性
    private num:number = 2;
    constructor() {
        super();
    }

    dispatch():void{
        var evt:Event = new Event(Box.EVENT_ID)
        // evt.num = this.num; //如果evt是Event类型,会报错,因为Event没有num属性,
        // 也就意味着在事件抛发的时候不能带一些数据出去
        this.dispatchEvent(evt)
    }

}

然后再Main.ts中导入,创建实例化对象b:

import Box from "./Box";

var b:Box = new Box()
// 让实例化对象侦听Box.EVENT_ID事件,并执行eventHandler回调函数
b.addEventListener(Box.EVENT_ID, eventHandler);
//Box实例化对象抛发事件
b.dispatch();
function eventHandler(e:Event):void{
    console.log(e);
}

在执行页面后

<script src="require.js" data-main="./js/event/Main"></script>

会这样报错:
在这里插入图片描述

因为EventTarget是一个Dom对象,而ts不属于Dom,如果以上写在js里是没有问题的,但是在ts里是无法运行的,所以我们可以简单重构下Event,可以完成简单的事件抛发侦听就可以了

新建一个事件类EmitterEvent,新建一个事件目标类EmitterTarget

EmitterEvent.ts

export default class EmitterEvent{
    //为了可以任意添加字符型属性
    [key:string]:any;
    //可以开放几个公有的事件属性,如target,currentTarget,为了简单就不加其他的属性了,而且必须只能是EmitterTarget类型因为只有EmitterTarget类型才能抛发和侦听事件
    public target?:EmitterTarget;
    public currentTarget?:EmitterTarget;
    public type?:string;
    //构造函数生成时将事件类型type带入,type是string类型
    constructor(type:string) {
        this.type = type;
}

EmitterTarget.ts


import EmitterEvent from "./EmitterEvent";
import IEventListener from "./IEventListener";
export default class EmitterTarget{
    //由于是先侦听后抛发,所以我们可以将事件类型和handler都保存起来,当我们dispatch的时候就激活这个函数
    //可以创建一个对象来保存
    // private eventDic:object = {}; //不能使用object类型,是因为object不能增加属性,会报错
    //为此我们新建一个接口IEventListener,让eventDic对应这个接口类型
    private eventDic:IEventListener = {};
    constructor() {

    }
    //提供事件抛发函数,public暴露
    public dispatchEvent(evt:EmitterEvent):void{
        //首先判断this.eventDic[evt.type as string]是否存在
        if(!evt.type) return;
        evt.currentTarget = this;
        evt.target = this;
        //console.log(evt)//EmitterEvent{type: "Box_event_id", num: 2}
        //this.eventDic[(evt.type as string)](evt);//evt.type在之前定义可能为空,所以给它断言为string
        //多个函数时,需要使用循环,
        if(!this.eventDic[evt.type]) return;
        for(var i = 0; i < this.eventDic[evt.type].length;i++){
            this.eventDic[evt.type][i](evt);
        }
    }
    //提供事件侦听函数,public暴露
    public addEventListener(type:string, handler:Function):void{
        //this.eventDic[type] = handler
        //console.log(this.eventDic)//Box_event_id: ƒ eventHandler(e)
        
    }
    //提供删除事件函数,public暴露
    public removeEventListener(type:string, handler:Function):void{
		if(!this.eventDic[type]) return;
        //数组长度为0,将数组释放掉,
        this.eventDic[type].length = 0;
    }
}

IEventListener.ts

export default interface IEventListener{
    //[key:string]:any;
    //函数的数组,为了可以一个事件绑定多个函数
    [key:string]:Array<Function>;
}

Box.ts

import EmitterEvent from "./EmitterEvent";
import EmitterTarget from "./EmitterTarget";
//现在Box应该继承的就不该是EventTarget了,应该是我们自己写的EmitterTarget类
export default class Box extends EmitterTarget{
// export default class Box extends EventTarget{
    //给Box设置一个静态只读字符串类型的事件类型变量
    static readonly EVENT_ID:string = "Box_event_id";
    //给一个私有属性
    private num:number = 2;
    constructor() {
        super();
    }

    dispatch():void{
        // var evt:Event = new Event(Box.EVENT_ID)
        // evt.num = this.num; //如果evt是Event类型,会报错,因为Event没有num属性,也就意味着在事件抛发的时候不能带一些数据出去
        var evt:EmitterEvent = new EmitterEvent(Box.EVENT_ID)
        evt.num = this.num;
		//this需要一个dispatchEvent()方法,那么就需要EmitterTarget提供一个
        this.dispatchEvent(evt)
    }

}

Main.ts

import Box from "./Box";
import EmitterEvent from "./EmitterEvent";

var b:Box = new Box()
// 让实例化对象侦听Box.EVENT_ID事件,并执行eventHandler回调函数
b.addEventListener(Box.EVENT_ID, eventHandler);
//侦听第二个
b.addEventListener(Box.EVENT_ID, eventHandler1);
//Box实例化对象抛发事件
b.dispatch();
//在这里e也应该为EmitterEvent类型
function eventHandler(e:EmitterEvent):void{
    console.log(e);
}
//第二个事件函数
function eventHandler1(e:EmitterEvent):void{
    console.log(e);
}

最后我们就完成了利用ts重构Event事件类;最后运行会得到事件对象
在这里插入图片描述

### Cocos开发中实现任务手势指引的方法 在Cocos开发中,实现任务手势指引通常涉及以下几个方面:检测用户的手势输入、处理手势逻辑以及通过视觉反馈引导玩家完成特定操作。虽然引用中的内容主要围绕ReactiveCocoa (RAC) 的响应式编程理念展开,但在Cocos开发环境中可以借鉴类似的事件驱动机制。 #### 一、手势识别的基础原理 手势识别的核心在于捕捉用户的触摸行为并将其转化为程序能够理解的动作序列。Cocos Creator 提供了 `onTouchStart`、`onTouchMove` 和 `onTouchEnd` 等方法用于监听触摸事件。这些方法允许开发者捕获屏幕上的触控数据,并进一步解析为具体的手势动作。 ```javascript // 示例代码:基本的触摸事件监听器 this.node.on('touchstart', function(event) { console.log("手指按下位置:", event.getLocation()); }); this.node.on('touchmove', function(event) { console.log("手指移动到:", event.getDelta()); }); this.node.on('touchend', function(event) { console.log("手指抬起"); }); ``` #### 二、基于状态机的任务指引设计 为了实现更复杂的手势指引功能,可以通过有限状态机(FSM)管理不同阶段的操作流程。例如,在游戏场景中提示玩家滑动某个方向解锁新区域: 1. **初始状态**:显示箭头动画指示目标方向; 2. **过渡状态**:当检测到接近预期轨迹的运动时更新UI反馈; 3. **成功/失败状态**:依据最终结果触发相应效果。 这种方法不仅提高了用户体验的一致性,还便于扩展支持多样的交互模式。 #### 三、利用信号流简化控制结构 尽管 RAC 是针对 iOS 平台的设计框架,其核心思想——即采用声明式的风格描述异步过程链路——同样适用于任何现代脚本环境下的应用架构重构工作。对于跨平台引擎如 Cocos 来说,则可通过观察者模式或者 Promise 链表等方式达成相似目的。 假设我们需要监控一系列连续发生的
04-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值