V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
sunny1688
V2EX  ›  程序员

请教一下 TypeScript 装饰器的用法

  •  
  •   sunny1688 · Aug 4, 2023 · 2549 views
    This topic created in 998 days ago, the information mentioned may be changed or developed.
    • 在写业务的时候经常需要订阅事件和取消订阅事件,通常是用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');
    
    
    Supplement 1  ·  Aug 5, 2023
    搞定了,要使用 装饰器 Stage-3 版本,我提供的代码是 Stage-1 版本,关于 Stage-3 版本可以看 #4 楼提供的链接
    14 replies    2023-08-06 14:47:06 +08:00
    kevin97
        1
    kevin97  
       Aug 4, 2023
    这样试试呢?添加 bind

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

    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  
       Aug 4, 2023
    @hsfzxjy 如果你想在构造时就自动注册,那就在构造函数里手动调用一下 registerAllEvents
    yetrun
        8
    yetrun  
       Aug 5, 2023
    装饰器尚在试验阶段,没有大规模铺开来用,别用。尤其是自己发布的包或者项目,别用,可能过几年就用不了了。
    zjccc
        9
    zjccc  
       Aug 5, 2023
    装饰器是作用在原型链上的,所以拿不到 this 。
    可以使用装饰器配合 Reflect-metadata 添加元数据(本质上和上面搞一个全局的 map 差不多),然后构造函数里面通过 this 拿到 prototype 上的方法,再调用 .call(this)
    19cm
        10
    19cm  
       Aug 5, 2023
    别用二货玩意,都要被淘汰的玩意
    hzzhzzdogee
        11
    hzzhzzdogee  
       Aug 5, 2023
    angular 不是大规模使用装饰器吗, 这个语法还是稳定的吧?
    nzbin
        12
    nzbin  
       Aug 5, 2023
    @hzzhzzdogee Angular 的装饰器只是语法糖,主要用于传递元数据,编译之后是没有的
    sunny1688
        13
    sunny1688  
    OP
       Aug 6, 2023
    @hzzhzzdogee
    @tianzi123

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

    不过还是没有达到我想要的版本,我用 stage-3 版本实现好了,只需要 @on('xx') 就能自动订阅事件,不需要在构造函数里面手动注册
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5955 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 65ms · UTC 02:13 · PVG 10:13 · LAX 19:13 · JFK 22:13
    ♥ Do have faith in what you're doing.