浅聊一下
装饰器(decorator
)最早在 Python
中被引入,它的主要作用是给一个已有的方法或类扩展一些新的行为,而不是去直接修改它本身.
在 ES2015
进入 Class
之后,当我们需要在多个不同的类之间共享或者扩展一些方法或行为的时候,代码会变得错综复杂,极其不优雅,这也就是装饰器被提出的一个很重要的原因
装饰器
在 Typescript
中我们需要在 tsconfig.json
里面开启支持选项
ts
// tsconfig.json
"experimentalDecorators": true
类装饰
想象一个场景,有一个Person类和一个addAge函数,addAge函数可以给Person添加一个年龄的属性,我们该如何来实现?
ts
function addAge(constructor: Function) {
constructor.prototype.age = 18;
}
class Person {
name: string;
age!: number;
constructor() {
this.name = 'juejin';
}
}
addAge(Person);
let person = new Person();
console.log(person.age);//18
- 我们首先定义了
addAge
函数,它接受一个构造函数作为参数,并给这个构造函数的原型添加一个名为age
的属性,赋值为18
。 - 然后我们定义了
Person
类,其中包含一个name
属性和一个构造函数,用于初始化name
。此时,我们不再需要在类定义中声明age
属性,因为它将通过原型链被添加。 - 接下来,我们调用
addAge(Person)
,将Person
构造函数传递给addAge
函数,这样就会在Person
的原型上添加age
属性。
注意这里我们并没有使用装饰器,那么使用装饰器又该如何来写
ts
function addAge(constructor: Function) {
constructor.prototype.age = 18;
}
@addAge
class Person {
name: string;
age!: number;
constructor() {
this.name = 'juejin';
}
}
let person = new Person();
console.log(person.age);//18
在 Person
类定义之前,使用了 @addAge
装饰器。这相当于在类定义完成后立即执行 addAge(Person)
,从而向 Person
的原型上添加了 age
属性。
总的来说,装饰器提供了一种优雅的方式来修改或增强类的行为,而无需改动类的原始代码。
属性、方法装饰器
一个类的属性和方法也可以被装饰
ts
function method(target: any, key: string,descriptor: PropertyDescriptor) {
console.log(target);
console.log('prop ' + key);
console.log('desc' + JSON.stringify(descriptor) + '\n\n');
descriptor.writable = false;
}
class Person {
name: string;
constructor() {
this.name = 'juejin';
}
@method
say(){
return 'instance method'
}
@method
static run() {
return 'static method'
}
}
let person = new Person();
person.say = function() {
return 'edit'
}
console.log(person.say());
- 首先定义了一个装饰器函数,接受三个参数
ts
function method(target: any, key: string, descriptor: PropertyDescriptor) {
console.log(target); // 打印目标对象(对于实例方法,是类的原型;对于静态方法,是类本身)
console.log('prop ' + key); // 打印被装饰的方法名
console.log('desc' + descriptor + '\n\n'); // 打印方法的描述符
descriptor.writable = false; // 设置描述符的writable属性为false,尝试修改方法会失败
}
target
: 对于实例方法,它是类的原型;对于静态方法,它是类本身。key
: 被装饰的方法的名称。descriptor
: 方法的属性描述符,它是一个对象,包含了该属性的特性(如value
,writable
,configurable
,enumerable
等)。
- Person类
在Person类中的say方法和run方法前加上装饰器函数
- 调用
想修改Person类的say方法,并且调用say方法查看是否改动
先来看看结果
首先可以看到我们并没有调用run方法,可是run方法被调用了
这是因为在类定义的时候,就会调用装饰器函数,即使我们没有实例化类对象。 而报错是因为我们无法修改一个只读属性
在属性/方法的装饰器定义过程中,与 class
的装饰器不同,我们的 method
函数中的参数变为了三个 target propertyKey descriptor
.这三个参数正是源于Object.defineProperty
上面的方法装饰器代码相当于下面:
ts
let descriptor = {
value: function() { return 'instance method'},
enumerable: false,
configurable: true,
writable: true
};
descriptor = readonly(Cat.prototype, "say", descriptor) || descriptor;
Object.defineProperty(Cat.prototype, "say", descriptor);
结尾
写到这里,该去吃饭了...