typescript之装饰器


定义

装饰器是一种特殊类型的声明,可以用在类声明、方法、属性或者参数上。它是用来给附着的主体进行装饰,添加额外的行为。
装饰器使用@expression这种形式,expression求值后必须为一个函数。

方法装饰器
1
2
3
4
5
6
let validate = function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// target: 静态方法指的是类的构造函数,实例方法指的是类的原型对象
// propertyKey: 方法的名字
// descriptor: 方法的属性描述符
{value: Function, writable: boolean, enumerable: boolean, configurable: boolean}
}
使用场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 假如实参为空时报错
const validate = function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// 保存原来的方法
let method = descriptor.value
// 重写原来的方法
descriptor.value = function (newValue: string) {
// 检查是否是空字符串
if (!newValue) {
throw Error('name is invalid')
} else {
// 否则调用原来的方法
method.call(this, newValue)
}
}
}

// 在定义class的时候validate已经执行了,将原方法保存并进行替换
class User{
name: string
constructor (name: string) {
this.name = name
}
@validate // 这里的target
changeName (newName:string) {
this.name = newName
}
/*
@validate // 这里的target为构造函数
static fn3 () {
console.log(333)
}
*/
}

let userA = new User('Ethan')
userA.changeName('Ethan66') // 此时执行的是新的方法
装饰器工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function god(name: string) {
console.log(`输出1: evaluated ${name}`)
// 这是装饰器,在 User 生成之后会执行
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log('输出2: called')
}
}

class User {
@god('test')
test () { }
}
// 输出1: evaluated test
// 输出2: called
类装饰器

类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。

1
2
3
4
5
6
7
function sealed(constructor: Function) {
Object.seal(constructor)
Object.seal(constructor.prototype)
}

@sealed
class User { }
访问器装饰器

和函数装饰器一样,只不过是装饰于访问器上的。

1
2
3
4
5
6
7
8
9
10
11
12
13
function god(name: string) {
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
}
}

class User {
private _name: string
// 装饰在访问器上
@god('tasaid.com')
get name () {
return this._name
}
}
属性装饰器
1
2
3
4
5
6
7
8
9
function god(target, propertyKey: string) {
// target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
// propertyKey: 成员的名字
}

class User {
@god
name: string
}
参数装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const required = function (target, propertyKey: string, parameterIndex: number) {
// target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
// propertyKey: 成员的名字
// parameterIndex: 参数在函数参数列表中的索引
}

class User {
private _name : string;
set name(@required name : string) {
this._name = name;
}
}

// 举例
// 定义一个私有 key
const requiredMetadataKey = Symbol.for('router:required')

// 定义参数装饰器,大概思路就是把要校验的参数索引保存到成员中
const required = function (target, propertyKey: string, parameterIndex: number) {
// 属性附加
const rules = Reflect.getMetadata(requiredMetadataKey, target, propertyKey) || []
rules.push(parameterIndex)
Reflect.defineMetadata(requiredMetadataKey, rules, target, propertyKey)
}

// 定义一个方法装饰器,从成员中获取要校验的参数进行校验
const validateEmptyStr = function (target, propertyKey: string, descriptor: PropertyDescriptor) {
// 保存原来的方法
let method = descriptor.value
// 重写原来的方法
descriptor.value = function () {
let args = arguments
// 看看成员里面有没有存的私有的对象
const rules = Reflect.getMetadata(requiredMetadataKey, target, propertyKey) as Array<number>
if (rules && rules.length) {
// 检查私有对象的 key
rules.forEach(parameterIndex => {
// 对应索引的参数进行校验
if (!args[parameterIndex]) throw Error(`arguments${parameterIndex} is invalid`)
})
}
return method.apply(this, arguments)
}
}

class User {
name: string
id: number
constructor(name:string, id: number) {
this.name = name
this.id = id
}

// 方法装饰器做校验
@validateEmptyStr
changeName (@required newName: string) { // 参数装饰器做描述
this.name = newName
}
}