一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
1. 类的装饰器
-
装饰器本身是一个函数
-
类装饰器接受的参数是构造函数
-
装饰器通过 @ 符号来使用
-
类的装饰器的执行时机:只在类定义时执行一次
function testDecorator(flag: boolean) {
if (flag) {
return function(constructor: any) {
constructor.prototype.getName = () => {
console.log('dell');
};
};
} else {
return function(constructor: any) {};
}
}
@testDecorator(true) // 这里也可以定义多个装饰器,但执行顺序是:先收集的装饰器后执行(即从下到上或者从右到左执行装饰器)
class Test {}
const test = new Test();
(test as any).getName();
以上类装饰器的写法容易理解,但没有代码提示的功能。以下是更复杂但标准合理的类装饰器的写法,且有代码提示的功能(factory工厂模式)
function testDecorator() {
// new (...args: any[]) => any这是一个构造函数,可以接受包含很多元素的数组作为参数,每个元素都是any类型,且构造函数的返回值也是any类型
return function<T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
name = 'lee';
getName() {
return this.name;
}
};
};
}
const Test = testDecorator()(
class {
name: string;
constructor(name: string) {
this.name = name;
}
}
);
const test = new Test('dell');
console.log(test.getName());// 此时就有代码提示功能了,且name为lee
2.方法装饰器
方法装饰器的参数有三个
// 普通方法,target 对应的是类的 prototype
// 静态方法,target 对应的是类的构造函数
function getNameDecorator(target: any, key: string, descriptor: PropertyDescriptor) { //key是用了装饰器的方法名
// console.log(target, key);
// descriptor.writable = true; // 该属性用来设置方法是否能被修改
descriptor.value = function() { // 这里是用来修改方法的返回值
return 'decorator';
};
}
class Test {
name: string;
constructor(name: string) {
this.name = name;
}
@getNameDecorator
getName() {
return this.name;
}
}
const test = new Test('dell');
console.log(test.getName());
3.访问器的装饰器
访问器装饰器的参数和方法装饰器的参数(有三个)都是一样的。
访问器的使用方式:只能在get或者set方法中的一个使用,不能两个都同时用
function visitDecorator(target: any, key: string, descriptor: PropertyDescriptor) { // 第一个参数是原型
// descriptor.writable = false;
}
class Test {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name() {
return this._name;
}
@visitDecorator
set name(name: string) {
this._name = name;
}
}
const test = new Test('dell');
test.name = 'dell lee';
console.log(test.name);
4.属性的装饰器
属性装饰器的参数只有两个。第一个是原型,第二个是key(即用了装饰器的属性名)
// 当装饰器返回一个descriptor对象时,就相当方法和访问器装饰器的第三个参数
// function nameDecorator(target: any, key: string): any {
// const descriptor: PropertyDescriptor = {
// writable: false
// };
// return descriptor;
// }
// test.name = 'dell lee';
// 修改的并不是实例上的 name, 而是原型上的 name
function nameDecorator(target: any, key: string): any {
target[key] = 'lee';
}
// name 放在实例上
class Test {
@nameDecorator
name = 'Dell';
}
const test = new Test();
console.log((test as any).__proto__.name);
5.参数装饰器
参数装饰器的参数有三个,分别是原型,方法名,参数所在的位置
// 原型,方法名,参数所在的位置
function paramDecorator(target: any, method: string, paramIndex: number) {
console.log(target, method, paramIndex);
}
class Test {
getInfo(name: string, @paramDecorator age: number) {
console.log(name, age);
}
}
const test = new Test();
test.getInfo('Dell', 30);
6.装饰器实际使用的小例子
异常捕获中使用方法装饰器可以提高代码的复用性,如下:
const userInfo: any = undefined;
function catchError(msg: string) { // 这里是工厂模式,主要是为了引入参数来指出具体是哪个不存在
return function(target: any, key: string, descriptor: PropertyDescriptor) {
const fn = descriptor.value;
descriptor.value = function() {
try {
fn();
} catch (e) {
console.log(msg);
}
};
};
}
class Test {
@catchError('userInfo.name 不存在')
getName() {
return userInfo.name;
}
@catchError('userInfo.age 不存在')
getAge() {
return userInfo.age;
}
@catchError('userInfo.gender 不存在')
getGender() {
return userInfo.gender;
}
}
const test = new Test();
test.getName();
test.getAge();