使用 TypeScript 自定义装饰器给类的属性增添监听器 Listener

举报
汪子熙 发表于 2022/06/19 18:39:59 2022/06/19
【摘要】 官网链接 语法type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;target:直接写在类的属性上?看个例子:function capitalizeFirstLetter(str: string) { return str.charAt(0).toUpperCase() + str...

官网链接

语法

type PropertyDecorator =
  (target: Object, propertyKey: string | symbol) => void;

target:直接写在类的属性上?

看个例子:

function capitalizeFirstLetter(str: string) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

function observable(target: any, key: string): any {
  // prop -> onPropChange
  const targetKey = "on" + capitalizeFirstLetter(key) + "Change";

  target[targetKey] =
    function (fn: (prev: any, next: any) => void) {
      let prev = this[key];
      Reflect.defineProperty(this, key, {
        set(next) {
          fn(prev, next);
          prev = next;
        }
      })
    };
}

class C {
  @observable
  foo = -1;

  @observable
  bar = "bar";
}

const c = new C();

c.onFooChange((prev, next) => console.log(`prev: ${prev}, next: ${next}`))
c.onBarChange((prev, next) => console.log(`prev: ${prev}, next: ${next}`))

c.foo = 100; // -> prev: -1, next: 100
c.foo = -3.14; // -> prev: 100, next: -3.14
c.bar = "baz"; // -> prev: bar, next: baz
c.bar = "sing"; // -> prev: baz, next: sing

编译通不过:

一种解决办法是,添加下图这种 dummy 方法,这种方法不推荐,因为缺乏灵活性:

另一种方法较通用,即为类添加通用的所谓的 index signature:

语法如下:

意思是类 C 可以拥有任意的属性,且属性名称为 string

运行时,target 的类型为类 C 的构造函数:

key 为属性名:


在下图第 15 行代码,直接给 C 的构造函数注入一个新的 on 监听函数:

这个监听函数的函数体,直到代码44行 onXXX 被调用时才会被执行:

给 C 对象实例的 foo 属性使用 Reflect.defineProperty API 设置一个 set 方法。

这样,每次该实例的 foo 属性被修改时,就触发其 set 函数:

在 set 函数实现体内,首先调用应用开发人员传入的 回调函数 fn,然后将 this[key] 设置为新的值 next.

运行时,c.foo = 100, 会导致 Reflect.defineProperty 注册在 foo 属性的 set 方法被触发:

在 set 函数里,我们再也不能访问到 C 实例的 foo 或者 bar 属性,但是通过闭包,能访问到其修改之前的原始值:

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。