JS 中的装饰器

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

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

function d(target) { return target }

@d
class A {}

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

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

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 方法

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

方法的装饰

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

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 中装饰器还可以作用于属性。

function d(target, name) {
    // target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    // name 属性的名字
}

class A() {
    @d
    greeting: string;
}
复制代码

参数装饰器

TypeScript 中还可以装饰参数,

function Query(target, name, index) {
    // target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    // name 方法的函数名
    // index 参数的索引 从 0 开始
    // 参数装饰器的返回值会被忽略。
}

class A() {
    fn(@Query query: Object) {
        
    }
}
复制代码

存取装饰器

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

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库被引入了,设计阶段添加的类型信息可以在运行时使用。

function D(...rest) {

}

class A {
  @D
  readonly a: string;
}

class B {
  fn(@D x: A) {
    console.log(x)
  }
}
复制代码

会被转义成

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 框架就大量使用到了这些元信息。

转载于:https://juejin.im/post/5c77f0f1518825629f387207

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值