一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
装饰器(decorator)面向对象的概念(java:注解、c#:特征)
装饰器用来解决什么问题?
分离关注点
假如有这样一个注册用户类
class User {
constructor(
public loginId: string, //必须5-10个字符串
public loginPwd: string, //必须是大于等于8个字符串
public age: number, // 必须是0-100之间的数字
public sex: 'man' | 'woman' //必须是 'man' | 'woman'
) { }
}
在用户注册时, 通常会写一个函数进行验证是否合法
function validetaUser(u: User) {
//对注册用户信息进行验证
if (u.loginId && u.loginId.length >= 5 && u.loginId.length <= 10) {
//loginId 验证通过
} else {
//loginId 验证失败
}
//其它属性验证
}
虽然这样看起来没有问题, 但是验证和数据分开了(还会存在代码的冗余), 就会出现这样的问题, 在书写用户属性时这一时刻我很清楚验证的条件, 但是我却需要去其它的函数进行验证规则的书写, 这不太符合大脑的思考方式。
上述问题的根源: 某些信息在定义时, 能都附加的信息有限。
装饰器就是为了解决这问题而存在的
装饰器可以带来额外的信息量, 达到分离关注点的问题
装饰器的作用:为某些属性、类、参数、方法提供元数据信息(metadata)
元数据: 描述数据的数据
装饰器的本质
在 JS 中, 装饰器是一个函数。
类装饰器
类装饰器的本质是一个函数, 该函数接受一个参数, 表示类本身(构造函数本身)
由于装饰器目前为止是处于实验中, 如果想在 TS 中使用需要tsconfig.json中配置compilerOptions.experimentalDecorators:true
使用装饰器@装饰器名(一个函数)
写一个装饰器函数
function test(target: new () => object) {
console.log(target)
}
@test
class A {
}
装饰器函数的运行时间: 在类定以后直接运行。
类装饰器可以具有的返回值:
- void: 使用默认返回
- class: 返回一个新的类
当有了装饰器之后 就可以玩很多丰富的效果
比如装饰器可以充当一个高阶函数
function test(target: new () => object) {
return class B extends target {
//做自己想做的事
}
}
@test
class A {
}
当方返回一下新的类时, 当执行cosnt a = new A() 实际上执行的是装饰器返回的类, 并不是这里的 A, 看一下编辑结果就明白了了
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
function test(target) {
return class B extends target {
};
}
let A = class A { //使用了一个变量进行保存
};
A = __decorate([ //重新赋值为 装饰器的返回 类
test
], A);
const a = new A();
但是及其不建议这样使用, TS 会得不到类型检查。
多个类装饰的运行顺序
type constructor = new (...arg: any[]) => object
function A1(target: constructor) {
console.log('A1')
}
function A2(target: constructor) {
console.log('A2')
}
@A1 //在执行A1
@A2 //先执行A2
class B {
}
当作用多个装饰器时, 装饰器的运行顺序为从下到上依次执行。
类成员装饰器
装饰属性
属性装饰器也是一个函数, 该函数需要两个参数:
-
如果是静态属性, 则为类本身、如果是实例属性, 则为类的原型
-
固定为一个字符串, 表示属性名
type constructor = new (...args: any[]) => any;
function test(target: object | constructor, key: string) {
console.log(target, key)
}
class A {
@test
name: string = 'A'
@test
static age: number = 18
}
console.log(123)
//输出结果
// {} name
// A age
// 123
装饰方法
方法装饰器也是一个函数, 该函数需要三个参数:
-
如果是静态方法, 则为类本身、如果是实例属性, 则为类的原型
-
定为一个字符串表示方法名
-
描述符对象(
Object.defineProperty(obj, prop, descriptor)) descriptor 配置对象就是属性描述符
type constructor = new (...args: any[]) => any;
function testFn(target: object | constructor, key: string, descriptor: PropertyDescriptor) {
console.log(target, key, descriptor)
}
class A {
@testFn
sayHello() {
}
}
//输出
{} sayHello {
value: [Function: sayHello],
writable: true,
enumerable: false,
configurable: true
}
参数装饰器
要求函数有三个参数:
-
如果方法是静态的, 则为类本身; 如果是实例方法, 则类类的原型
-
方法名称
-
在参数列表中索引
function test(target: any, methid: string, index: number) {
console.log(target, methid, index)
}
class User {
sayHello(@test name: string) {
}
static m(@test m: string){
}
}
// 输出
// {} sayHello 0
// User m 0
总结
以上就是对装饰器大致的了解, 至于怎么灵活的使用就要发挥想象力