JS 中的装饰器还是一个提案,需要 babel 才可以使用。它还是一项实验性特性,在未来的版本中可能会发生改变。
装饰器是一个函数,它只能作用于类不能作用于(因为函数提升),它以一个@
符号开头,如下:
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 多个装饰器执行顺序是,
- 参数装饰器,然后是方法装饰器,访问符装饰器或属性装饰器应用到每个实例成员
- 参数装饰器,然后是方法装饰器,访问符装饰器或属性装饰器应用到每个静态成员
- 参数装饰器应用到构造函数
- 类装饰器应用到类
是从里到外的执行顺序。
元数据
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 框架就大量使用到了这些元信息。