0%

decorator

typescript 深入理解装饰器

这篇文章总结的很好

装饰器使用一种特殊的 @expression 声明使用,它必须被解析为一个函数,以在运行时调用

装饰器工厂

如果你想要更灵活的使用装饰器,可以使用装饰器工厂方式,装饰器工厂返回一个装饰器函数,以在运行时被调用。使用装饰器工厂意味着你可以向装饰器工厂函数传入参数,以生成不同行为的装饰器函数

1
2
3
4
5
6
7
function color(value: string) {
// this is the decorator factory
return function (target) {
// this is the decorator
// do something with 'target' and 'value'...
};
}

多个装饰器

装饰器调用顺序
实例属性的方法参数装饰器,方法装饰器,存取装饰器,属性装饰器
静态属性的方法参数装饰器,方法装饰器,存取装饰器,属性装饰器
构造函数的参数装饰器
类装饰器


  1. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each instance member.
  2. Parameter Decorators, followed by Method, Accessor, or Property Decorators are applied for each static member.
  3. Parameter Decorators are applied for the constructor.
  4. Class Decorators are applied for the class.

定义(工厂方法):相同装饰器按从上到下方式执行工厂方法返回装饰器函数前的语句
执行:相同装饰器在运行时按照从下到上的顺序执行

  1. The expressions for each decorator are evaluated top-to-bottom.
  2. The results are then called as functions from bottom-to-top.

PropertyDescriptor

允许对一个属性的描述进行检索,在 js 中属性由一个字符串类型的名字和一个属性描述符构成

  1. value 该属性的值
  2. writable 当且仅当属性的值可以被改变时为 true。(仅针对数据属性描述有效)
  3. get 获取该属性的访问器函数(getter)。如果没有访问器, 该值为 undefined。(仅针对包含访问器或设置器的属性描述有效)
  4. set 获取该属性的设置器函数(setter)。 如果没有设置器, 该值为 undefined。(仅针对包含访问器或设置器的属性描述有效)
  5. configurable 当且仅当指定对象的属性描述可以被改变或者属性可被删除时,为 true。
  6. enumerable 当且仅当指定对象的属性可以被枚举出时,为 true。

类装饰器

被应用于类的构造函数并且可以被用来观察,修改或者替换类的定义。类修饰器不能被用在声明文件中。

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/**

- 属性装饰器
- @param targetPrototype 构造函数的原型
- @param propName 属性名称
*/
function propDecorator1(targetPrototype: any, propName: string) {
console.log("属性装饰器 1:", targetPrototype, propName);
}

function propDecorator2(params: string) {
console.log("属性装饰器 2 before:", params);
return function (targetPrototype: any, propName: string) {
// targetPrototype[propName]是在原型链上查找实例属性,永远为 undefined
console.log(
"属性装饰器 2:",
targetPrototype,
propName,
targetPrototype[propName]
);
targetPrototype[propName] = "会在原型上面添加该属性并赋值,不是实例对象";
};
}

function propDecorator3(params: string) {
console.log("属性装饰器 3 before:", params);
return function (targetPrototype: any, propName: string) {
console.log("属性装饰器 3:", targetPrototype, propName);
};
}

/**

- 方法装饰器
- @param targetPrototype 构造函数的原型
- @param methodName 方法名称
- @param descr 方法的描述
*/
function methodDecorator1(
targetPrototype: any,
methodName: string,
descriptor: PropertyDescriptor
) {
console.log("方法装饰器 1:", targetPrototype, methodName, descriptor);
}

let methodDecorator2: any = function (params: string) {
console.log("方法装饰器 2 before:", params);
return function (
targetPrototype: any,
methodName: string,
descriptor: PropertyDescriptor
) {
console.log("方法装饰器 2:", targetPrototype, methodName, descriptor);
};
};

let methodDecorator3: any = function (params: string) {
console.log("方法装饰器 3 before:", params);
return function (
targetPrototype: any,
methodName: string,
descriptor: PropertyDescriptor
) {
console.log("方法装饰器 3:", targetPrototype, methodName, descriptor);
let originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
args = args.map((item) => (item += " test"));
return originalMethod.apply(this, args);
};
return descriptor;
};
};

/**

- 方法参数装饰器
- @param targetPrototype 构造函数的原型
- @param methodName 方法名称
- @param paramIndex 参数在 arguments 中的下标
*/
function paramDecorator1(
targetPrototype: any,
methodName: string,
paramIndex: number
) {
console.log("方法参数装饰器 1:", targetPrototype, methodName, paramIndex);
}

function paramDecorator2(params: string) {
console.log("方法参数装饰器 2 before:", params);
return function (
targetPrototype: any,
methodName: string,
paramIndex: number
) {
console.log("方法参数装饰器 2:", targetPrototype, methodName, paramIndex);
};
}

function paramDecorator3(params: string) {
console.log("方法参数装饰器 3 before:", params);
return function (
targetPrototype: any,
methodName: string,
paramIndex: number
) {
console.log("方法参数装饰器 3:", targetPrototype, methodName, paramIndex);
};
}

/**

- 静态属性修饰器
- @param targetPrototype
- @param propName
*/
function staticPropDecorator(param: string) {
console.log("静态属性修饰器 before:", param);
return function (targetPrototype: any, propName: string) {
console.log(
"静态属性修饰器:",
targetPrototype,
propName,
targetPrototype[propName]
);
targetPrototype[propName] = "静态属性初始值被修改了!";
};
}

/**

- 静态方法修饰器
- @param targetPrototype
- @param methodName
- @param descriptor
*/
function staticMethodDecorator(param: string) {
console.log("静态方法修饰器 before:", param);
return function (
targetPrototype: any,
methodName: string,
descriptor: PropertyDescriptor
) {
console.log("静态方法修饰器:", targetPrototype, methodName, descriptor);
};
}

/**

- 类装饰器
- @param targetClass
*/
function classDecorator1(constructor: Function) {
console.log("类装饰器 1:", constructor);
}

function classDecorator2(params: string) {
console.log("类装饰器 2 before:", params);
return function (constructor: Function) {
console.log("类装饰器 2:", constructor);
};
}

function classDecorator3(params: string) {
console.log("类装饰器 3 before:", params);
return function (constructor: Function) {
console.log("类装饰器 3:", constructor);
};
}

/**

- 存取器装饰器
- @param target
- @param propertyKey
- @param PropertyDescriptor
*/
function accessorDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.get = () => "存取器修饰后的值"
}

@classDecorator1
@classDecorator2("params2")
@classDecorator3("params3")
class Test {
@propDecorator1
@propDecorator2("param2")
@propDecorator3("param3")
public msg: string = "属性初始值";

@staticPropDecorator("静态属性")
static title: string = "静态属性初始值";

constructor(msg: string) {
this.msg = msg;
}

@methodDecorator1
@methodDecorator2("param2")
@methodDecorator3("param3")
toString(
@paramDecorator1 str1: string,
@paramDecorator2("param2") str2: string,
@paramDecorator3("param3") str3: string
) {
console.log("toString:", str1, str2, str3);
}

@staticMethodDecorator("静态方法")
static staticToString() {
console.log(this.title);
}

@accessorDecorator
get msg1() {
return this.msg
}

}

let t: any = new Test("this is a msg.");
t.toString("ss", "dd", "ff"); //methodDecorator3 装饰器对该方法进行了重写
console.log(t.msg, t.proto, t.msg, t.msg1);
Test.staticToString();
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
属性装饰器2 before: param2
属性装饰器3 before: param3
属性装饰器3: Test { toString: [Function] } msg
属性装饰器2: Test { toString: [Function] } msg undefined
属性装饰器1: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } msg
方法装饰器2 before: param2
方法装饰器3 before: param3
方法参数装饰器2 before: param2
方法参数装饰器3 before: param3
方法参数装饰器3: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString 2
方法参数装饰器2: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString 1
方法参数装饰器1: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString 0
方法装饰器3: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString { value: [Function],
writable: true,
enumerable: true,
configurable: true }
方法装饰器2: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString { value: [Function],
writable: true,
enumerable: true,
configurable: true }
方法装饰器1: Test { toString: [Function], msg: '会在原型上面添加该属性并赋值,不是实例对象' } toString { value: [Function],
writable: true,
enumerable: true,
configurable: true }
静态属性修饰器 before: 静态属性
静态属性修饰器: function Test(msg) {
this.msg = "属性初始值";
this.msg = msg;
} title 静态属性初始值
静态方法修饰器 before: 静态方法
静态方法修饰器: function Test(msg) {
this.msg = "属性初始值";
this.msg = msg;
} staticToString { value: [Function],
writable: true,
enumerable: true,
configurable: true }
类装饰器2 before: params2
类装饰器3 before: params3
类装饰器3: function Test(msg) {
this.msg = "属性初始值";
this.msg = msg;
}
类装饰器2: function Test(msg) {
this.msg = "属性初始值";
this.msg = msg;
}
类装饰器1: function Test(msg) {
this.msg = "属性初始值";
this.msg = msg;
}
toString: ss test dd test ff test
this is a msg. undefined this is a msg. 存取器修饰后的值
静态属性初始值被修改了!