@@ -106,12 +106,13 @@ type Decorator = (
106
106
context : {
107
107
kind: string ;
108
108
name: string | symbol ;
109
- addInitializer(initializer : () => void ): void ;
110
-
111
- // 以下属性只在某些使用场合存在:
112
- static: boolean ;
113
- private: boolean ;
114
- access: {get: () => unknown , set: (value : unknown ) => void };
109
+ addInitializer? (initializer : () => void ): void ;
110
+ static? : boolean ;
111
+ private? : boolean ;
112
+ access: {
113
+ get? (): unknown ;
114
+ set? (value : unknown ): void ;
115
+ };
115
116
}
116
117
) => void | ReplacementValue ;
117
118
```
@@ -132,26 +133,28 @@ function decorator(
132
133
133
134
上面是一个装饰器函数,其中第二个参数` context ` 的类型就可以写成` ClassMethodDecoratorContext ` 。
134
135
135
- ` context ` 对象有以下属性 。
136
+ ` context ` 对象的属性,根据所装饰对象的不同而不同,其中只有两个属性( ` kind ` 和 ` name ` )是必有的,其他都是可选的 。
136
137
137
- (1)` kind ` :字符串,表示装饰器类型 ,可能取以下的值。
138
+ (1)` kind ` :字符串,表示所装饰对象的类型 ,可能取以下的值。
138
139
139
140
- 'class'
140
141
- 'method'
141
142
- 'getter'
142
143
- 'setter'
143
- - 'accessor'
144
144
- 'field'
145
+ - 'accessor'
145
146
146
- 这表示一共有六种类型的装饰器。
147
+ 这表示一共有六种类型的装饰器。本章只介绍前五种装饰器,最后一种 ` accessor ` 暂时略过。
147
148
148
149
(2)` name ` :字符串或者 Symbol 值,所装饰对象的名字,比如类名、属性名等。
149
150
150
- (3)` addInitializer() ` :函数,用来在类的初始化阶段,对方法进行一些处理。以前,这些处理通常放在构造函数里面,早于方法本身执行,现在改为放在装饰器的` context ` 对象里面,具体例子请参阅《方法装饰器》一节。
151
+ (3)` addInitializer() ` :函数,用来添加类的初始化逻辑。以前,这些逻辑通常放在构造函数里面,对方法进行初始化,现在改成以函数形式传入` addInitializer() ` 方法。注意,` addInitializer() ` 没有返回值。
152
+
153
+ (4)` private ` :布尔值,表示所装饰的对象是否为类的私有成员。
151
154
152
- 注意, ` addInitializer() ` 函数没有返回值 。
155
+ (5) ` static ` :布尔值,表示所装饰的对象是否为类的静态成员 。
153
156
154
- (4) ` private ` :布尔值,表示所装饰的方法或属性,是否为私有 。
157
+ (6) ` access ` :一个对象,包含了某个值的 get 和 set 方法 。
155
158
156
159
## 类装饰器
157
160
@@ -168,49 +171,41 @@ type ClassDecorator = (
168
171
) => Function | void ;
169
172
```
170
173
171
- 请看下面的例子。
174
+ 类装饰器接受两个参数:` value ` (当前类本身)和` context ` (上下文对象)。其中,` context ` 对象的` kind ` 属性固定为字符串` class ` 。
175
+
176
+ 类装饰器一般用来对类进行操作,可以不返回任何值,请看下面的例子。
172
177
173
178
``` typescript
174
- class InstanceCollector {
175
- instances = new Set ();
176
- install = (value : any , {kind }: any ) => {
177
- if (kind === ' class' ) {
178
- const _this = this ;
179
- return function (... args : any []) {
180
- const inst = new value (... args );
181
- _this .instances .add (inst );
182
- return value ;
183
- } as unknown as typeof MyClass ;
184
- }
185
- return ;
186
- };
179
+ function Greeter(value , context ) {
180
+ if (context .kind === ' class' ) {
181
+ value .prototype .greet = function () {
182
+ console .log (' 你好' );
183
+ };
184
+ }
187
185
}
188
186
189
- const collector = new InstanceCollector ();
190
-
191
- @collector .install
192
- class MyClass {}
193
-
194
- const inst1 = new MyClass ();
195
- const inst2 = new MyClass ();
196
- const inst3 = new MyClass ();
187
+ @Greeter
188
+ class User {}
197
189
198
- collector .instances // new Set([inst1, inst2, inst3])
190
+ let u = new User ();
191
+ u .greet (); // "你好"
199
192
```
200
193
201
- 上面示例中,类装饰器` @collector.install ` 将所有实例加入一个集合变量 ` collector.instances ` 。
194
+ 上面示例中,类装饰器` @Greeter ` 在类 ` User ` 的原型对象上,添加了一个 ` greet() ` 方法,实例就可以直接使用该方法 。
202
195
203
- 类装饰器返回的函数,会作为新的构造函数 。
196
+ 类装饰器可以返回一个函数,替代当前类的构造方法 。
204
197
205
198
``` typescript
206
199
function countInstances(value : any , context : any ) {
207
200
let instanceCount = 0 ;
201
+
208
202
const wrapper = function (... args : any []) {
209
203
instanceCount ++ ;
210
204
const instance = new value (... args );
211
205
instance .count = instanceCount ;
212
206
return instance ;
213
207
} as unknown as typeof MyClass ;
208
+
214
209
wrapper .prototype = value .prototype ; // A
215
210
return wrapper ;
216
211
}
@@ -223,13 +218,16 @@ inst1 instanceof MyClass // true
223
218
inst1 .count // 1
224
219
```
225
220
226
- 上面示例实现了实例的计数。为了确保` wrapper() ` 的返回值是` MyClass ` 的示例,特别加入` A ` 行,确保两者的原型对象是一致的。否则,新的构造函数` wrapper ` 的原型对象,与` MyClass ` 不同,通不过` instanceof ` 运算符。
221
+ 上面示例中,类装饰器` @countInstances ` 返回一个函数,替换了类` MyClass ` 的构造方法。新的构造方法实现了实例的计数,每新建一个实例,计数器就会加一,并且对实例添加` count ` 属性,表示当前实例的编号。
222
+
223
+ 注意,上例为了确保新构造方法继承定义在` MyClass ` 的原型之上的成员,特别加入` A ` 行,确保两者的原型对象是一致的。否则,新的构造函数` wrapper ` 的原型对象,与` MyClass ` 不同,通不过` instanceof ` 运算符。
227
224
228
- 类装饰器也可以直接返回一个新的类 。
225
+ 类装饰器也可以返回一个新的类,替代原来所装饰的类 。
229
226
230
227
``` typescript
231
228
function countInstances(value : any , context : any ) {
232
229
let instanceCount = 0 ;
230
+
233
231
return class extends value {
234
232
constructor (... args : any []) {
235
233
super (... args );
@@ -249,7 +247,7 @@ inst1.count // 1
249
247
250
248
上面示例中,` @countInstances ` 返回一个` MyClass ` 的子类。
251
249
252
- 下面的例子是通过类装饰器,禁止使用` new ` 命令调用类 。
250
+ 下面的例子是通过类装饰器,禁止使用` new ` 命令新建类的实例 。
253
251
254
252
``` typescript
255
253
function functionCallable(
@@ -295,38 +293,37 @@ type ClassMethodDecorator = (
295
293
) => Function | void ;
296
294
```
297
295
298
- 它的上下文对象` context ` 有以下属性。
296
+ 根据上面的类型,方法装饰器是一个函数,接受两个参数:` value ` 和` context ` 。
297
+
298
+ 参数` value ` 是方法本身,参数` context ` 是上下文对象,有以下属性。
299
299
300
- - static:布尔值,表示是否为静态方法。
301
- - private:布尔值,表示是否为私有方法。
300
+ - ` kind ` :值固定为字符串` method ` ,表示当前为方法装饰器。
301
+ - ` name ` :所装饰的方法名,类型为字符串或 Symbol 值。
302
+ - ` static ` :布尔值,表示是否为静态方法。该属性为只读属性。
303
+ - ` private ` :布尔值,表示是否为私有方法。该属性为只读属性。
302
304
- access:函数,表示方法的存取器,但是只能用来取值(只有` get() ` 方法),不能用来赋值(不能定义` set() ` 方法)。
303
305
304
- ``` typescript
305
- class C {
306
- @trace
307
- toString() {
308
- return ' C' ;
309
- }
310
- }
306
+ 方法装饰器会改写类的原始方法,实质等同于下面的操作。
311
307
308
+ ``` typescript
312
309
function trace(decoratedMethod ) {
313
- // 此处略
310
+ // ...
314
311
}
315
- ```
316
-
317
- 方法装饰器的实质是执行下面的操作。
318
312
319
- ``` typescript
320
313
class C {
314
+ @trace
321
315
toString() {
322
316
return ' C' ;
323
317
}
324
318
}
325
319
326
- C .prototype .toString = trace (C .prototype .toString );
320
+ // `@trace` 等同于
321
+ // C.prototype.toString = trace(C.prototype.toString);
327
322
```
328
323
329
- 如果装饰器返回一个新的函数,就会替代所装饰的对象。
324
+ 上面示例中,` @trace ` 是方法` toString() ` 的装饰器,它的效果等同于最后一行对` toString() ` 的改写。
325
+
326
+ 如果方法装饰器返回一个新的函数,就会替代所装饰的原始函数。
330
327
331
328
``` typescript
332
329
function replaceMethod() {
@@ -383,12 +380,16 @@ function log(originalMethod:any, context:ClassMethodDecoratorContext) {
383
380
384
381
const person = new Person (' 张三' );
385
382
person .greet ()
386
- // "LOG: Entering method 'greet'."
387
- // "Hello, my name is 张三."
388
- // "LOG: Exiting method 'greet'."
383
+ // "LOG: Entering method 'greet'."
384
+ // "Hello, my name is 张三."
385
+ // "LOG: Exiting method 'greet'."
389
386
```
390
387
391
- 下面是装饰器上下文对象的` addInitializer() ` 方法的例子。类的方法往往会在构造方法里面,进行` this ` 的绑定。
388
+ 上面示例中,装饰器` @log ` 的返回值是一个函数` replacementMethod ` ,替代了原始方法` greet() ` 。在` replacementMethod() ` 内部,通过执行` originalMethod.call() ` 完成了对原始方法的调用。
389
+
390
+ 方法装饰器的参数` context ` 对象里面,有一个` addInitializer() ` 方法。它是一个钩子方法,用来在类的初始化阶段,添加回调函数,这个回调函数就是作为` addInitializer() ` 的参数传入的。
391
+
392
+ 下面是` addInitializer() ` 方法的一个例子。我们知道,类的方法往往需要在构造方法里面,进行` this ` 的绑定。
392
393
393
394
``` typescript
394
395
class Person {
@@ -404,9 +405,14 @@ class Person {
404
405
console .log (` Hello, my name is ${this .name }. ` );
405
406
}
406
407
}
408
+
409
+ const g = new Person (' 张三' ).greet ;
410
+ g () // "Hello, my name is 张三."
407
411
```
408
412
409
- 上面例子中,构造方法将` greet() ` 方法绑定了` this ` ,这行代码必须放在构造方法里面。现在,它可以移到` addInitializer() ` 。
413
+ 上面例子中,类` Person ` 的构造方法内部,将` this ` 与` greet() ` 方法进行了绑定。如果没有这一行,将` greet() ` 赋值给变量` g ` 进行调用,就会报错了。
414
+
415
+ ` this ` 的绑定必须放在构造方法里面,因为这必须在类的初始化阶段完成。现在,它可以移到方法装饰器的` addInitializer() ` 里面。
410
416
411
417
``` typescript
412
418
function bound(
@@ -424,6 +430,8 @@ function bound(
424
430
425
431
上面示例中,绑定` this ` 转移到了` addInitializer() ` 方法里面。
426
432
433
+ 下面再看一个例子,通过` addInitializer() ` 将选定的方法名,放入一个集合。
434
+
427
435
``` typescript
428
436
function collect(
429
437
value ,
@@ -449,7 +457,7 @@ const inst = new C();
449
457
inst .@collect // new Set(['toString', Symbol.iterator])
450
458
```
451
459
452
- 上面示例中,装饰器 ` @collect ` 会将所装饰的成员名字,加入一个 Set 集合` collectedMethodKeys ` 。
460
+ 上面示例中,方法装饰器 ` @collect ` 会将所装饰的成员名字,加入一个 Set 集合` collectedMethodKeys ` 。
453
461
454
462
## 属性装饰器
455
463
@@ -469,36 +477,32 @@ type ClassFieldDecorator = (
469
477
) => (initialValue : unknown ) => unknown | void ;
470
478
```
471
479
472
- 注意,装饰器的第一个参数` value ` 的类型是` undefined ` ,这意味着这个参数实际上是不存在的,即不能从 ` value ` 获取目标属性的值 。
480
+ 注意,装饰器的第一个参数` value ` 的类型是` undefined ` ,这意味着这个参数实际上没用的,装饰器不能从 ` value ` 获取所装饰属性的值,要用下文的方法获取。另外,第二个参数 ` context ` 对象的 ` kind ` 属性的值为字符串 ` field ` ,而不是“property”或“attribute”,这一点是需要注意的 。
473
481
474
- 如果要获取属性的值,必须使用存取器,请看下面的例子 。
482
+ 属性装饰器的返回值是一个函数,该函数会自动执行,用来对所装饰属性进行初始化。该函数的参数是所装饰属性的初始值,该函数的返回值是所装饰属性的最终值 。
475
483
476
484
``` typescript
477
- let acc;
478
-
479
- function exposeAccess(
480
- value , {access }
481
- ) {
482
- acc = access ;
485
+ function logged(value , context ) {
486
+ const { kind, name } = context ;
487
+ if (kind === ' field' ) {
488
+ return function (initialValue ) {
489
+ console .log (` initializing ${name } with value ${initialValue } ` );
490
+ return initialValue ;
491
+ };
492
+ }
483
493
}
484
494
485
495
class Color {
486
- @exposeAccess
487
- name = ' green'
496
+ @logged name = ' green' ;
488
497
}
489
498
490
- const green = new Color ();
491
- green .name // 'green'
492
-
493
- acc .get .call (green ) // 'green'
494
-
495
- acc .set .call (green , ' red' );
496
- green .name // 'red'
499
+ const color = new Color ();
500
+ // "initializing name with value green"
497
501
```
498
502
499
- 上面示例中,` @exposeAccess ` 是 ` name ` 属性的装饰器,它的第二个参数就是 ` name ` 的上下文对象,其中 ` access ` 属性包含了取值器( ` get ` )和存值器( ` set ` ),可以对 ` name ` 属性进行取值和赋值 。
503
+ 上面示例中,属性装饰器 ` @logged ` 装饰属性 ` name ` 。 ` @logged ` 的返回值是一个函数,该函数用来对属性 ` name ` 进行初始化,它的参数 ` initialValue ` 就是属性 ` name ` 的初始值 ` green ` 。新建实例对象 ` color ` 时,该函数会自动执行 。
500
504
501
- 下面的例子是更改属性的初始值 。
505
+ 属性装饰器的返回值函数,可以用来更改属性的初始值 。
502
506
503
507
``` typescript
504
508
function twice() {
@@ -514,6 +518,35 @@ const inst = new C();
514
518
inst .field // 6
515
519
```
516
520
521
+ 上面示例中,属性装饰器` @twice ` 返回一个函数,该函数的返回值是属性` field ` 的初始值乘以2,所以属性` field ` 的最终值是6。
522
+
523
+ 属性装饰器的上下文对象` context ` 的` access ` 属性,提供所装饰属性的存取器,请看下面的例子。
524
+
525
+ ``` typescript
526
+ let acc;
527
+
528
+ function exposeAccess(
529
+ value , {access }
530
+ ) {
531
+ acc = access ;
532
+ }
533
+
534
+ class Color {
535
+ @exposeAccess
536
+ name = ' green'
537
+ }
538
+
539
+ const green = new Color ();
540
+ green .name // 'green'
541
+
542
+ acc .get (green ) // 'green'
543
+
544
+ acc .set (green , ' red' );
545
+ green .name // 'red'
546
+ ```
547
+
548
+ 上面示例中,` access ` 包含了属性` name ` 的存取器,可以对该属性进行取值和赋值。
549
+
517
550
## getter 装饰器,setter 装饰器
518
551
519
552
getter 装饰器和 setter 装饰器的类型描述如下。
0 commit comments