# 装饰器
WARNING
基础
- 装饰器定义
- 装饰器工厂
- 装饰器组合
- 装饰器求值
类装饰器
方法装饰器
访问器装饰器
属性装饰器
参数装饰器
tsconfig.json 将装饰器的配置打开
{
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
1
2
3
4
2
3
4
装饰器是一种新的声明,能够作用于类的声明、类的方法、属性、参数上。 装饰器需要紧挨着要装饰的函数前面。
// 语法格式
function setProp(target) {
// target 是要修饰的内容
// ...
}
@setProp
1
2
3
4
5
6
7
2
3
4
5
6
7
装饰器工厂,也是一个函数,返回值是一个函数,返回的函数作为装饰器的调用函数。
function setProp() {
return function (target) {
// ...
}
}
@setProp()
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
装饰器可以组合使用,多个装饰器可以同时装饰同一个目标上
// 从下往上依次装饰
@setProp
@setName
@setAge
target
1
2
3
4
5
2
3
4
5
// 如果是装饰器工厂的话,先上到下一次求值,然后在下往上一次装饰
@setProp()
@setName()
@setAge()
target
1
2
3
4
5
2
3
4
5
function setName () {
console.log('get setname-1')
return (target: any) => {
console.log('setname-2', target)
}
}
function setAge() {
console.log('set age-1')
return (target: any) => {
console.log('age-2', target)
}
}
@setName()
@setAge()
class ClassDes {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

注意图中的执行顺序
类装饰器,在运行的时候会被当做函数调用,有唯一的参数,即被装饰的类
let sign = null
function setName (name: string) {
return (target: new() => any) => {
sign = target
console.log(target.name) // ClassDes
}
}
@setName('xiaoyu')
class ClassDes {
constructor() {
}
}
console.log(sign === ClassDes) // true
console.log(sign === ClassDes.prototype.constructor) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
通过装饰器可以修改类的原型对象和构造函数
function addName(constructor: new() => any) {
constructor.prototype.name = '你好'
}
@addName
class ClassD {
}
// 定义同名,合并
interface ClassD {
name: string
}
let d = new ClassD()
console.log(d.name) // 你好
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
会使用装饰器里返回的类,替换被装饰器修饰的类
function classDecoator<T extends { new(...args: any[]): {} }> (target:T){
return class extends target {
newProperty = 'new newProperty'
hello = '你好'
}
}
@classDecoator
class Greeter {
property = 'property'
hello: string = ''
constructor(m: string) {
this.hello = m
}
}
console.log(new Greeter('world'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

function classDecoator2(target:any): any {
return class {
newProperty = 'new newProperty'
hello = '你好'
}
}
@classDecoator2
class Greeter2 {
property = 'property'
hello: string = ''
constructor(m: string) {
this.hello = m
}
}
// 被装饰器返回的类替换了
console.log(new Greeter2('world'))
// {newProperty: 'new newProperty', hello: '你好'}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
方法装饰器,处理类中的方法、可以处理方法的属性描述符、处理方法的定义。 在运行的时候被当做函数调用,包含三个参数。
如果装饰的是静态成员方法,第一个参数代表的是类的构造函数。
如果装饰的是实例成员的时候,第一个参数代表的是类的原型对象。
第二个参数代表的是成员的名字、第三个参数成员的属性描述符
属性描述符
// configurable // 属性可配置
// writeable // 属性可写
// enumerable // 属性可枚举
1
2
3
2
3
interface ObjWithAnyKeys {
[key: string]: any
}
let objData: ObjWithAnyKeys = {}
Object.defineProperty(objData, 'name', {
value: '你好',
writable: false, // 不可写
configurable: true, // 可配置
enumerable: true // 可枚举的
})
console.log(objData.name, 'objData') // 你好
objData.name = '12' // 报错提示不可写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
function enumerable (bool: boolean) {
return (target: any, propertyName: string, descriptor: PropertyDescriptor) => {
console.log(target, '要修饰的---target')
console.log(propertyName, '要修饰的---propertyName')
console.log(descriptor, '要修饰的---descriptor')
descriptor.enumerable = bool
}
}
class ClassF {
constructor(public age: number) {
}
// 修饰器
@enumerable(false)
getAge() {
return this.age
}
}
let cf = new ClassF(18)
console.log(cf)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

访问器装饰器(set & get)
TS 不允许分别装饰同一个成员的 set 和 get 访问器
@xxx // 会同时装饰 set 和get
set() {}
get() {}
1
2
3
2
3
function enumerable2 (bool: boolean) {
return (target: any, propertyName: string, descriptor: PropertyDescriptor) => {
console.log(target, '要修饰的--target-enumerable2')
console.log(propertyName, '要修饰的--propertyName-enumerable2')
console.log(descriptor, '要修饰的--descriptor-enumerable2')
descriptor.enumerable = bool
}
}
class ClassG {
private _name: string = ''
constructor(name: string) {
this._name = name
}
@enumerable2(false) // name 属性是不可枚举的
get name() {
return this._name
}
set name(value: string) {
this._name = value
}
}
let fg = new ClassG('你好')
console.log(fg.name)
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
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

属性装饰器
修饰属性的,用来判断某个类中是否声明了某个属性
function printPropertyName (target: any, propertyName: string) {
console.log(propertyName, '要修饰的--propertyName')// name
}
class ClassH {
@printPropertyName
name: string = '你好呀'
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
参数装饰器
function required(target: any, propertyName: string, index: number) {
console.log(propertyName, '是要修饰的-propertyName', index) // getInfo 是要修饰的-propertyName 1
}
class ClassI {
name: string = '你好'
age: number = 18
getInfo(prefix: string, @required infotype: string): any {
return prefix + '--' + infotype
}
}
interface ClassI {
[key: string]: string | number | Function
}
let infoI = new ClassI()
console.log(infoI.getInfo('haha', 'age'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19