类的装饰器
初始化项目
npm init -y
tsc --init
npm install ts-node -D
npm install typescript
类的装饰器
装饰器本身是一个函数
装饰器通过@符号来使用
打开支持
支持装饰器语法
装饰器会在类定义好,就立即执行。并不是实例化的时候进行装饰。
类装饰器接受的参数是构造函数
多个装饰器的执行顺序
函数包装开关
覆盖类的属性
添加类属性
function testDecorator() { 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());
方法装饰器
// 普通方法,target 对应的是类的 prototype // 静态方法,target 对应的是类的构造函数 function getNameDecorator(target: any, key: string, descriptor: PropertyDescriptor) { // 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());
为函数添加描述符
变更函数
访问器的装饰器
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);
属性的装饰器
// 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);
手写属性描述器
修改的并不是实例上的 name, 而是原型上的 name
参数装饰器
// 原型,方法名,参数所在的位置 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);
装饰器实际使用的小例子
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();
reflect-metadata
import 'reflect-metadata'; class User { @Reflect.metadata('data', 'test') @Reflect.metadata('data1', 'test') getName() {} } class Teacher extends User {} console.log(Reflect.getOwnMetadataKeys(User.prototype, 'getName')); console.log(Reflect.getOwnMetadataKeys(Teacher.prototype, 'getName'));
装饰器的执行顺序
import 'reflect-metadata'; function showData(target: typeof User) { for (let key in target.prototype) { const data = Reflect.getMetadata('data', target.prototype, key); console.log(data); } } function setData(dataKey: string, msg: string) { return function(target: User, key: string) { Reflect.defineMetadata(dataKey, msg, target, key); }; } @showData class User { @setData('data', 'age') getName() {} @setData('data', 'age') getAge() {} }