V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sunny1688
V2EX  ›  程序员

请教一下 TypeScript 装饰器的用法

  •  
  •   sunny1688 · 267 天前 · 1426 次点击
    这是一个创建于 267 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • 在写业务的时候经常需要订阅事件和取消订阅事件,通常是用emitter.onemitter.off 来完成,如果要绑定 this 的话,会使用 emitter.on('key',this.xxx.bind(this)); 用于绑定 this

    • 最近看到装饰器 @expression,在想是不是可以用装饰器的方式来给某个方法自动订阅事件,但遇到了 this 指向的问题,在装饰器内无法访问到 this ,所以想请教一下各位大神应该如何实现这种想法,谢谢,下面是我的一个实现(如果不需要 this 绑定的话是可以的,但怎么可能会不要 this 绑定呢)

    
    import EventEmitter from 'node:events';
    
    const emitter = new EventEmitter();
    
    const on = (eventName: string) => {
        return (target: Object, methodName: string, descriptor: PropertyDescriptor) => {
    
            // 直接这样绑定可以运行
            emitter.on(eventName, descriptor.value);
    
    
            const value = descriptor.value;
    
            // 如果是这种写法,就可以有 this ,但不会执行到 get()里面,写了也没用
            return {
                configurable: true,
                enumerable: true,
                get() {
                    // 这里的 this 就是 Logic ,但根本不会执行到这里面
                    const bound = value.bind(this);
                    emitter.on(eventName, bound);
                    return bound;
                }
            } as PropertyDescriptor
        }
    }
    
    
    const off = (eventName: string) => {
        return (target: Object, methodName: string, descriptor: PropertyDescriptor) => {
            // todo
        }
    }
    
    class Logic {
        value = 1;
    
        @on('eventKey')
        onStart() {
            console.log('onStart');
    
            // this 是 undefined ,我想在把 this 绑定到当前对象上,应该如何在装饰器上处理?
            console.log(this);  //
        }
    
    
        @off('start')
        onDestroy() {
    
        }
    }
    
    new Logic();
    
    // 某些时刻会触发,这里是否模拟一下
    emitter.emit('eventKey');
    
    
    第 1 条附言  ·  265 天前
    搞定了,要使用 装饰器 Stage-3 版本,我提供的代码是 Stage-1 版本,关于 Stage-3 版本可以看 #4 楼提供的链接
    14 条回复    2023-08-06 14:47:06 +08:00
    kevin97
        1
    kevin97  
       267 天前
    这样试试呢?添加 bind

    ```js
    // 直接这样绑定可以运行
    emitter.on(eventName, descriptor.value.bind(target));
    ```
    sunny1688
        2
    sunny1688  
    OP
       267 天前
    target 不是 Logic 实例 @kevin97
    NICE20991231
        3
    NICE20991231  
       266 天前
    方法装饰器负责添加元信息(就是一个配置...),具体的由 class 装饰器实现,可以试下
    zbinlin
        4
    zbinlin  
       266 天前
    zsj1029
        5
    zsj1029  
       266 天前 via iPhone
    Ts 的装饰器还是实验阶段吧,没正式发布吧
    hsfzxjy
        6
    hsfzxjy  
       266 天前
    帮楼主一整套写好了

    https://www.typescriptlang.org/play?experimentalDecorators=true&target=99#code/MYewdgzgLgBAogNwKZigYQIYBssCMPADWAshgA4DSSAnjALwwDK1AtriFgBQCUA3AFBRqZJPGSpMOfEVJl6MWQB5oAJwCWYAOYAaGADEArmGBQ14ANoBdAHwD+oSLHDzOScVAByGFkgBcMVQ1NbnprGE4oDBVNJCh-AHlcACskE10fKAALEAATLx9-QK1dHKQIYHUyKBAVfwAFFRARFSEAETKKtSqakLowgG9+GGGYB2gYQiwMCAh5SOjYmGmYI0IwEAB3MCXZ-phzRBR0bDwCEnIqakt-Q4kT6XO5AF8BEZgsReBcWRv3SVOZORXiM1AAzcKJFImAB0mWm8S2nEm0wguluxykZ1kl24IUGbzeX1k8mRMwOf3uWIuNEswOGTxgSCwEFE+IJwyJ5HkYCQGwUQKG7JgkNSUGhpVBGiQDSaSBa1CRUxmaIpmMBlBoujZQpGDklmgMKgwuA+-lB2BZ2kFOuGKAMPiNJr8MHNzKQVptIwQ2AMzs5ZGtBKe3EDT0DY1gX1mDH90JiUFc7nySBCAH5U-taeHcBBoWQDBBMpxSuVKtUVNDvVhfXxs7JoSyE24jsndFHa2H7OBxgAlJCaNTQOVIHL-B4QS7yZhsDg8ARCEQwPsDocqEdjs7R-bN1DJwpQdTFUa4fyGYymcCWKwCUFGExmbZrldQOUAQRw6IgnBAyX8GDA1C6E+g4vmutThGA3jOkUOgumAp53heYC9GECAgGoOR4oGYIQskoqwvCiI-kkujLiBw6jpSRATjQuIwGuUCGmAdKjN2sDAauI7+GRnGUWqhBbtega3ueD4wHCYA5B8MrVAuSCcAAHn+AFYTqEYwGQjTVPIIowvGMkgHJ8SgoptY6jhnCaYZID0HQDBgAYOAhAxTEsW8ElSdKWmGcI8lWdUZlChZACEulinCEAImAlneSqRwbuqOLObErmBoSbHHj8YjxVRjzyP5IDkjl-HYjSbm6t85DQqCNRwAQRacFGug7p4UEoceuY1SodXAEWXyhDA2o2up7BGDk8hfNCuAaDk37JIFnocaBcqJi2UG6KNkkLTaS0UXmBZFuYLWtjAm05JY21vMG21hu5-6eQZclzUk21heKSCSjyMrNEIz2kf25FrnxAICZcWppSMdoOsapouha7oQxy4D6oaMPOq6lqIzAVa+v4u1Ax6IzBvwnYifezhGPj75YJ+z3KYBKxgPjcr+JwkEFAEB5BG2J76IhD7tWhGGqbqGXMzk3EA7xCUCfIxHmDxy3A+OlxZm84vVbV9WcJw5js+6x4XQNlNS8tKhs+tx64ixGsfFoWTyAADAInbAEqswAEKDYGmlqN6L70UgGA5OAWC0DjogMAATM7gYAALgJwABELWvknIZvKCACMPDe2p3YcEg0JYCAmjJx7lY+pHSe6Fkg615kg6V9WKYsWGrvuzAaCMgpL6SZ7ecjL7-uiGuweh+HVdR-IAAsUcsQn0Up+4acZyMoJR7nQ3pZAhfF6XydoM3vrT3QNcwHXuYR1HN0k-w-Bk0hgfPsO5vHVB+6HrBXwIaJ4Ai8MAA9IAmAgBCK0APtGgBouUAG9ygALm0ADGK4YC4fH3mXAABorCiMBFAABJ+iTX1k8MIXVGTuBgEnPB78fBPCTmgteQCQEQJgYAYBjAAUrkg3eKCS7oIAJJgDQoQIIMA8EEKgk8aEEi6HZh4HfB+-MKZM1Nq-Vau4P6cy-jzX+5NkKDwYWAqBcDAAAqRwiAe9uGcDQQAVUUS-IG2CRG4GhIQ4hNRSFHHIZQpMYjaEhk7F2ThRdzEAHIAC0YSwlBJDCNbkvIYAewEFTD87gvy4CAkosCtYTa2OprTVJjNmYqFrGMMxB9QnhJCZE-x4xgAxL5GgBJ6ScnJMamk2xcpMk2MBk0o4X5gC6CyYDdpAhilcNKeUip3AgA
    hsfzxjy
        7
    hsfzxjy  
       266 天前
    @hsfzxjy 如果你想在构造时就自动注册,那就在构造函数里手动调用一下 registerAllEvents
    yetrun
        8
    yetrun  
       266 天前
    装饰器尚在试验阶段,没有大规模铺开来用,别用。尤其是自己发布的包或者项目,别用,可能过几年就用不了了。
    zjccc
        9
    zjccc  
       266 天前
    装饰器是作用在原型链上的,所以拿不到 this 。
    可以使用装饰器配合 Reflect-metadata 添加元数据(本质上和上面搞一个全局的 map 差不多),然后构造函数里面通过 this 拿到 prototype 上的方法,再调用 .call(this)
    tianzi123
        10
    tianzi123  
       265 天前
    别用二货玩意,都要被淘汰的玩意
    hzzhzzdogee
        11
    hzzhzzdogee  
       265 天前
    angular 不是大规模使用装饰器吗, 这个语法还是稳定的吧?
    nzbin
        12
    nzbin  
       265 天前
    @hzzhzzdogee Angular 的装饰器只是语法糖,主要用于传递元数据,编译之后是没有的
    sunny1688
        13
    sunny1688  
    OP
       265 天前
    @hzzhzzdogee
    @tianzi123

    正如 12 楼说的那样,装饰器只是语法糖,那些 @符号编译后是没有的,现在都 stage-3 阶段了,不会淘汰的
    sunny1688
        14
    sunny1688  
    OP
       265 天前
    @hsfzxjy 很感谢!

    不过还是没有达到我想要的版本,我用 stage-3 版本实现好了,只需要 @on('xx') 就能自动订阅事件,不需要在构造函数里面手动注册
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2708 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 10:45 · PVG 18:45 · LAX 03:45 · JFK 06:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.