# 装饰器

WARNING

  • 基础

    • 装饰器定义
    • 装饰器工厂
    • 装饰器组合
    • 装饰器求值
  • 类装饰器

  • 方法装饰器

  • 访问器装饰器

  • 属性装饰器

  • 参数装饰器

tsconfig.json 将装饰器的配置打开

{
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
}
1
2
3
4

装饰器是一种新的声明,能够作用于类的声明、类的方法、属性、参数上。 装饰器需要紧挨着要装饰的函数前面。

// 语法格式
function setProp(target) {
	// target 是要修饰的内容
	// ...
}

@setProp
1
2
3
4
5
6
7

装饰器工厂,也是一个函数,返回值是一个函数,返回的函数作为装饰器的调用函数。


function setProp() {
	return function (target) {
		// ...
	}
}

@setProp()

1
2
3
4
5
6
7
8
9

装饰器可以组合使用,多个装饰器可以同时装饰同一个目标上

// 从下往上依次装饰
@setProp
@setName
@setAge
target
1
2
3
4
5
// 如果是装饰器工厂的话,先上到下一次求值,然后在下往上一次装饰
@setProp()
@setName()
@setAge()
target
1
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

静态图片

注意图中的执行顺序

类装饰器,在运行的时候会被当做函数调用,有唯一的参数,即被装饰的类


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

通过装饰器可以修改类的原型对象和构造函数

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

会使用装饰器里返回的类,替换被装饰器修饰的类

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

静态图片

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

方法装饰器,处理类中的方法、可以处理方法的属性描述符、处理方法的定义。 在运行的时候被当做函数调用,包含三个参数。

如果装饰的是静态成员方法,第一个参数代表的是类的构造函数。

如果装饰的是实例成员的时候,第一个参数代表的是类的原型对象。

第二个参数代表的是成员的名字、第三个参数成员的属性描述符

属性描述符

// configurable // 属性可配置
// writeable // 属性可写
// enumerable // 属性可枚举
1
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
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

静态图片

访问器装饰器(set & get)

TS 不允许分别装饰同一个成员的 set 和 get 访问器

@xxx  // 会同时装饰 set 和get
set() {}
get() {}
1
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

静态图片

属性装饰器

修饰属性的,用来判断某个类中是否声明了某个属性

function printPropertyName (target: any, propertyName: string) {
	console.log(propertyName, '要修饰的--propertyName')// name
}

class ClassH {
	@printPropertyName
	name: string = '你好呀'
}
1
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