JS 中的装饰器

2022-10-08 13:49:45 浏览数 (1)

JS 中的装饰器还是一个提案,需要 babel 才可以使用。它还是一项实验性特性,在未来的版本中可能会发生改变。

装饰器是一个函数,它只能作用于类不能作用于(因为函数提升),它以一个@符号开头,如下:

代码语言:javascript复制
function d(target) { return target }

@dclass A {}// 它相当于把类作为参数传递给 d 函数,然后再返回一个被函数修改过的类// 等同于A = d(A) || A

它还可以传递自定义参数和使用多个装饰器

代码语言:javascript复制
function a(p) {    console.log(1)    return (target => {        console.log(2)        return target
    })
}function b(target) {    console.log(3)    return target
}

@b
@a(1)class A {}// 打印顺序是 1 2 3

多个装饰器除了一行写一个,也可以全都写在一行@b @a(1)

在 react-redux 应用中使用装饰器可以这样使用 connect 方法

代码语言:javascript复制
@connect(mapStateToProps, mapDispatchToProps)export default class Comp extends Component {}

方法的装饰

装饰器除了作用于类,还可以作用于类的方法。

代码语言:javascript复制
function d(target, name, desc) {    // target 是类的原型对象
    // name 是函数的名字
    // desc 是属性描述符
    let oldValue = desc.value;

    desc.value = function() {        console.log(`Calling ${name} with`, arguments);        return oldValue.apply(this, arguments);
    };    return desc    // 如果方法装饰器返回一个值,它会被用作方法的属性描述符。}class A {
    @d
    fn () {}
}

在 TypeScript 中方法装饰器的 target 参数,对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

属性装饰器

在 TypeScript 中装饰器还可以作用于属性。

代码语言:javascript复制
function d(target, name) {    // target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    // name 属性的名字}class A() {
    @d    greeting: string;
}

参数装饰器

TypeScript 中还可以装饰参数,

代码语言:javascript复制
function Query(target, name, index) {    // target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    // name 方法的函数名
    // index 参数的索引 从 0 开始
    // 参数装饰器的返回值会被忽略。}class A() {    fn(@Query query: Object) {
        
    }
}

存取装饰器

TypeScript 中装饰器也可以装饰存取声明函数。

代码语言:javascript复制
function d(target, name, desc) {    // target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    // name 函数名
    // desc 属性描述符
    return desc    // 如果方法装饰器返回一个值,它会被用作属性描述符}class A() {
    @d    get a() {}
}

装饰器求值

TypeScript 多个装饰器执行顺序是,

  1. 参数装饰器,然后是方法装饰器,访问符装饰器或属性装饰器应用到每个实例成员
  2. 参数装饰器,然后是方法装饰器,访问符装饰器或属性装饰器应用到每个静态成员
  3. 参数装饰器应用到构造函数
  4. 类装饰器应用到类

是从里到外的执行顺序。

元数据

reflect-metadata 库是来支持实验性的metadata API,这个库还不是ECMAScript (JavaScript)标准的一部分。

要使用元数据需要先开启experimentalDecorators emitDecoratorMetadata选项。

当启用后,只要reflect-metadata库被引入了,设计阶段添加的类型信息可以在运行时使用。

代码语言:javascript复制
function D(...rest) {

}class A {
  @D
  readonly a: string;
}class B {  fn(@D x: A) {    console.log(x)
  }
}

会被转义成

代码语言:javascript复制
function D(...rest) {
}class A {
}__decorate([
    D,    __metadata("design:type", String)
], A.prototype, "a", void 0);class B {    fn(x) {        console.log(x);
    }
}__decorate([    __param(0, D),    __metadata("design:type", Function),    __metadata("design:paramtypes", [A]),    __metadata("design:returntype", void 0)
], B.prototype, "fn", null);

它加入了__metadata函数,将装饰器的元信息存取起来,这样就可以随时获取出来。

nodejs 的 nestjs 框架就大量使用到了这些元信息。

0 人点赞