突然想到装饰器, 也是因为公司的项目BFF层这边使用的nestjs, nestjs中使用了大量的装饰器。 这样的写法简洁明了, 强约定性也便于后期的维护。
装饰器的好处
- 好的装饰器就是代码注释;
- 帮助代码解耦;
- 提高代码的复用性;
- 代码更加简洁, 更便于阅读;
装饰器的使用
装饰器目前可以理解为对类class的一种修饰和修改。装饰器本身是一个函数,可以放在类,类方法,类方法的参数定义前面。
function testD(target){
return target.test = true
}
@testD
class Test {}
console.log(Test.test) // true
这里通过装饰器完成了对class内部属性的一个添加操作。
装饰类
上面的代码就是对类的一种装饰,它接受一个target参数就是类Test本身。
如果想要传参可以这样:
function testD(params){
return function(target){
return target.test = params
}
}
@testD(true)
class Test{}
上面的代码都是在类身上添加静态属性, 如果想要添加实例属性可以考虑添加到Test.prototype上。
装饰类的方法
class Person {
@nonenumerable
get kidCount() { return this.children.length; }
}
function nonenumerable(target, name, descriptor) {
descriptor.enumerable = false;
return descriptor;
}
装饰类的方法支持三个参数, 第一个参数是类的原型对象,第二个参数是所要装饰的属性名, 第三个参数是属性的描述对象, 上面的装饰器实现了属性不可遍历。
装饰类的方法的参数
函数参数装饰器会接收三个参数:
- 类的原型
- 参数所处的函数名称
- 参数在函数中形参中的位置(函数签名中的第几个参数)
装饰函数参数主要应用场景还是做参数的校验和转换。
Reflect.metadata
在定义类或者类方法的时候,可以设置一些元数据,我们可以获取到在类与类方法上添加的元数据,用的方法就是 Reflect Metadata。元数据指的是描述东西时用的数据。
@Reflect.metadata('name', 'zhangsan')
class People{}
const data = Reflect.getMetadata('name', People)
console.log(data) //zhangsan
上面是直接使用Reflect.metadata作为装饰器, 要想实现比较复杂的功能, 也可以考虑自定义:
function Name(params){
return (target) => {
Reflect.defineMetadata('name', params, target)
}
}
@Name('zhangsan')
class People{}
const data = Reflect.getMetadata('name', People)
console.log(data) //zhangsan
项目中使用装饰器
目前装饰器还在提案阶段,babel是支持编译装饰器的, 要想在项目中使用, 可以安装插件babel-plugin-transform-decorators-legacy, @babel/plugin-proposal-decorators;
如果ide是vscode, 在setting.json中配置"javascript.implicitProjectConfig.experimentalDecorators": true
要使用Reflect.metadata需要安装reflect-metadata包,如果是ts项目,并且配置tsconf.json:
{
"compilerOptions": {
"experimentalDecorators": true ,
"emitDecoratorMetadata": true
},
}
后话
装饰器是在编译时执行的,而不是运行时。 可以理解为装饰器就是一个编译时执行的对类进行修饰的函数。
装饰器为什么不能用于函数: 因为存在函数提升, 而类没有。
参考: