Skip to content

Commit 7c2493e

Browse files
committed
feat: update articles
1 parent f8c2ffa commit 7c2493e

1 file changed

Lines changed: 125 additions & 1 deletion

File tree

‎JavaScript/TypeScript/类型使用/Decorator.md‎

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export function Log() {
4444
}
4545
```
4646

47+
对于类的函数的装饰器函数,依次接受的参数为:
48+
49+
- target:如果修饰的是类的实例函数,那么 target 就是类的原型。如果修饰的是类的静态函数,那么 target 就是类本身。
50+
- key: 该函数的函数名。
51+
- descriptor:该函数的描述属性,比如 configurable、value、enumerable 等。
52+
4753
其使用方式如下:
4854

4955
```js
@@ -70,4 +76,122 @@ export class Entity {
7076
}
7177
```
7278

73-
# 元数据操作
79+
# 类的装饰器
80+
81+
当装饰函数直接修饰类的时候,装饰函数接受唯一的参数,这个参数就是该被修饰类本身。
82+
83+
```ts
84+
let temple;
85+
function foo(target) {
86+
console.log(target);
87+
temple = target;
88+
}
89+
@foo
90+
class P {
91+
constructor() {}
92+
}
93+
94+
const p = new P();
95+
temple === P; //true
96+
```
97+
98+
此外,在修饰类的时候,如果装饰函数有返回值,该返回值会重新定义这个类,也就是说当装饰函数有返回值时,其实是生成了一个新类,该新类通过返回值来定义。
99+
100+
```ts
101+
function foo(target) {
102+
return class extends target {
103+
name = 'Jony';
104+
sayHello() {
105+
console.log('Hello ' + this.name);
106+
}
107+
};
108+
}
109+
@foo
110+
class P {
111+
constructor() {}
112+
}
113+
114+
const p = new P();
115+
p.sayHello(); // 会输出Hello Jony
116+
```
117+
118+
上面的例子可以看到,当装饰函数 foo 有返回值时,实际上 P 类已经被返回值所代表的新类所代替,因此 P 的实例 p 拥有 sayHello 方法。
119+
120+
# 属性与参数装饰器
121+
122+
装饰函数修饰类的属性时,在类实例化的时候调用属性的装饰函数,举例来说:
123+
124+
```ts
125+
function foo(target, name) {
126+
console.log('target is', target);
127+
console.log('name is', name);
128+
}
129+
class P {
130+
@foo
131+
name = 'Jony';
132+
}
133+
const p = new P();
134+
//会依次输出 target is f P() name is Jony
135+
```
136+
137+
这里对于类的属性的装饰器函数接受两个参数,对于静态属性而言,第一个参数是类本身,对于实例属性而言,第一个参数是类的原型,第二个参数是指属性的名字。类函数的参数装饰器可以修饰类的构建函数中的参数,以及类中其他普通函数中的参数。该装饰器在类的方法被调用的时候执行,下面来看实例:
138+
139+
```ts
140+
function foo(target, key, index) {
141+
console.log('target is', target);
142+
console.log('key is', key);
143+
console.log('index is', index);
144+
}
145+
class P {
146+
test(@foo a) {}
147+
}
148+
const p = new P();
149+
p.test('Hello Jony');
150+
// 依次输出 f P() , test , 0
151+
```
152+
153+
类函数参数的装饰器函数接受三个参数,依次为类本身,类中该被修饰的函数本身,以及被修饰的参数在参数列表中的索引值。上述的例子中,会依次输出 f P() 、test 和 0。再次明确一下修饰函数参数的装饰器函数中的参数含义:
154+
155+
- target: 类本身
156+
- key:该参数所在的函数的函数名
157+
- index: 该参数在函数参数列表中的索引值
158+
159+
# 注解与元数据操作
160+
161+
所谓注解的定义就是:为相应的类附加元数据支持。所谓元数据可以简单的解释,就是修饰数据的数据,比如一个人有 name,age 等数据属性,那么 name 和 age 这些字段就是为了修饰数据的数据,可以简单的称为元数据。通过注解添加元数据,然后在装饰器中获取这些元数据,完成对类、类的方法等等的修改,可以在装饰器中添加元数据的支持,比如可以可以在装饰器工厂函数以及装饰器函数中添加元数据支持等。
162+
163+
可以通过 reflect-metadata 包来实现对于元数据的操作。首先我们来看 reflect-metadata 的使用,首先定义使用元数据的函数:
164+
165+
```ts
166+
const formatMetadataKey = Symbol('format');
167+
168+
function format(formatString: string) {
169+
return Reflect.metadata(formatMetadataKey, formatString);
170+
}
171+
172+
function getFormat(target: any, propertyKey: string) {
173+
return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
174+
}
175+
```
176+
177+
这里的 format 可以作为装饰器函数的工厂函数,因为 format 函数返回的是一个装饰器函数,上述的方法定义了元数据 Sysmbol("format"),用 Sysmbol 的原因是为了防止元数据中的字段重复,而 format 定义了取元数据中相应字段的功能。接着我们来在类中使用相应的元数据:
178+
179+
```ts
180+
class Greeter {
181+
@format('Hello, %s')
182+
name: string;
183+
184+
constructor(name: string) {
185+
this.name = message;
186+
}
187+
sayHello() {
188+
let formatString = getFormat(this, 'name');
189+
return formatString.replace('%s', this.name);
190+
}
191+
}
192+
193+
const g = new Greeter('Jony');
194+
console.log(g.sayHello());
195+
```
196+
197+
在上述中,我们在 name 属性的装饰器工厂函数,执行`@Format("Hello, %s")`,返回一个装饰器函数,且该装饰器函数修饰了 Greeter 类的 name 属性,将“name”属性的值写入为"Hello, %s"。然后再 sayHello 方法中,通过 getFormat(this,"name") 取到 formatString 为“Hello,%s”.

0 commit comments

Comments
 (0)