(预测未来最好的方法就是把它创造出来——尼葛洛庞帝)
装饰器
装饰器一种更现代的代码模式,通过使用@的形式注入在属性,寄存器,方法,方法参数和类中,比如在Angular,Nestjs和midway等流行框架中也都用到了装饰器。
官方说明
对装饰器更详细的相关说明
tc39提案
tc39提案一共分为五个阶段
Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
Stage 1 - 建议(Proposal):这是值得跟进的。
Stage 2 - 草案(Draft):初始规范。
Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。
装饰器目前已经在阶段3,相信不久的将来,js中也会支持装饰器。在typescript5.0中已经完全支持了装饰器。
该提案进度更详细的相关说明
装饰器实践
tsconfig.json
{ "compilerOptions": { "target": "ES6", // 为发出的JavaScript设置JavaScript语言版本,并包含兼容的库声明 "experimentalDecorators": true, // 启用对遗留实验装饰器的实验支持 "module": "ES6", // 指定生成的模块代码 "esModuleInterop": true, // 发出额外的JavaScript以简化对导入CommonJS模块的支持。这为类型兼容性启用了“allowSyntheticDefaultImports” "moduleResolution": "node", // 指定TypeScript如何从给定的模块说明符中查找文件 "outDir": "dist", // 为所有发出的文件指定输出文件夹 "rootDir": "src", // 指定源文件中的根文件夹 }, "include": [ // 需要编译的目录文件 "src/**/*", ], "exclude": [ // 需要排除的目录文件 "node_modules" ] }
复制
官方更详细的配置说明
类装饰器
typescript
// 通过装饰器对类进行扩展,减少对代码侵入性和业务间的耦合性 // constructor参数为类的构造函数 const classExtends = () => { const ClassDecorator = (constructor: Function) => { console.log('ClassDecorator---'); // 扩展类属性 constructor.prototype.name = 'ClassDecoratorName'; // 扩展类方法 constructor.prototype.run = () => { console.log('ClassDecorator run test'); } }; interface Test { name: string; run (): void; } @ClassDecorator class Test { } new Test().run(); const obj = { name: 'adsa' }; Reflect.get(obj, 'name'); }; classExtends(); // 通过装饰器入参的形式对类进行扩展,使用参数可以对业务进行更强的定制化处理 const classExtendByParams = () => { const ClassDecorator = (param: string) => { return function (constructor: Function) { // 针对入参做不同的处理 constructor.prototype.run = () => { if (param === 'agent') { console.log('classExtendByParams run agent'); } else if (param === 'user') { console.log('classExtendByParams run user'); } }; } }; interface Test { name: string; run (): void; } @ClassDecorator('agent') class Test { } new Test().run(); }; classExtendByParams(); // 通过装饰器工厂方法进行扩展,工厂方法装饰器可以和类型更好的兼容 const classExtendOfFactory = () => { const ClassDecorator = (param: string) => { return function <T extends new (...args: any[]) => any> (constructor: T) { return class extends constructor { run () { if (param === 'agent') { console.log('classExtendOfFactory run agent'); } else if (param === 'user') { console.log('classExtendOfFactory run user'); } }; } } }; const Test = ClassDecorator('user')( class { } ); new Test().run(); }; classExtendOfFactory();
复制
javascript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; // 通过装饰器对类进行扩展,减少对代码侵入性和业务间的耦合性 // constructor参数为类的构造函数 const classExtends = () => { const ClassDecorator = (constructor) => { console.log('ClassDecorator---'); // 扩展类属性 constructor.prototype.name = 'ClassDecoratorName'; // 扩展类方法 constructor.prototype.run = () => { console.log('ClassDecorator run test'); }; }; let Test = class Test { }; Test = __decorate([ ClassDecorator ], Test); new Test().run(); const obj = { name: 'adsa' }; Reflect.get(obj, 'name'); }; classExtends(); // 通过装饰器入参的形式对类进行扩展,使用参数可以对业务进行更强的定制化处理 const classExtendByParams = () => { const ClassDecorator = (param) => { return function (constructor) { // 针对入参做不同的处理 constructor.prototype.run = () => { if (param === 'agent') { console.log('classExtendByParams run agent'); } else if (param === 'user') { console.log('classExtendByParams run user'); } }; }; }; let Test = class Test { }; Test = __decorate([ ClassDecorator('agent') ], Test); new Test().run(); }; classExtendByParams(); // 通过装饰器工厂方法进行扩展,工厂方法装饰器可以和类型更好的兼容 const classExtendOfFactory = () => { const ClassDecorator = (param) => { return function (constructor) { return class extends constructor { run() { if (param === 'agent') { console.log('classExtendOfFactory run agent'); } else if (param === 'user') { console.log('classExtendOfFactory run user'); } } ; }; }; }; const Test = ClassDecorator('user')(class { }); new Test().run(); }; classExtendOfFactory();
复制
方法装饰器
typescript
/** * 入参解释 * target: 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象 * propertyKey: 属性的名称 * descriptor: 属性的描述器 */ /** * PropertyDescriptor参数解释 * PropertyDescriptor的参数各项为js的属性描述符,在创建变量或方法等对象时,js会默认赋予这些描述符 * 详细的阅读https://www.tektutorialshub.com/javascript/javascript-property-descriptors-enumerable-writable-configurable/ * descriptor 参数 * value 方法自身 * writable 该方法是否可以被变更 * enumerable 是否可以被枚举 * configurable 决定是否可配置,如果为false,则value,writable,enumerable不能被修改 */ // 通过装饰器对方法进行扩展,减少对代码侵入性和业务间的耦合性 const methodExtends = () => { const MethodDecorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { // 获取方法本身 const method = descriptor.value; // 对该方法的生命周期进行操作 descriptor.value = (...args) => { console.log('MethodDecorator before run'); const data = method.call(this, args); console.log('MethodDecorator after run'); return data; }; }; class Test { @MethodDecorator method () { return ';;;;'; } } console.log(new Test().method()); }; methodExtends(); // 通过装饰入参的形式对方法进行扩展,使用参数可以对业务进行更强的定制化处理 const methodExtendsByParams = () => { const MethodDecorator = (param: string) => { console.log('methodExtendsByParams', param); return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { // 获取方法本身 const method = descriptor.value; // 对该方法的生命周期进行操作 descriptor.value = (...args) => { console.log('before run'); const data = method.call(this, args); console.log('after run'); return data; }; } }; class Test { @MethodDecorator('user') method () { return ';;;;'; } } console.log(new Test().method()); }; methodExtendsByParams();
复制
javascript
/** * 入参解释 * target: 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象 * propertyKey: 属性的名称 * descriptor: 属性的描述器 */ /** * PropertyDescriptor参数解释 * PropertyDescriptor的参数各项为js的属性描述符,在创建变量或方法等对象时,js会默认赋予这些描述符 * 详细的阅读https://www.tektutorialshub.com/javascript/javascript-property-descriptors-enumerable-writable-configurable/ * descriptor 参数 * value 方法自身 * writable 该方法是否可以被变更 * enumerable 是否可以被枚举 * configurable 决定是否可配置,如果为false,则value,writable,enumerable不能被修改 */ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; // 通过装饰器对方法进行扩展,减少对代码侵入性和业务间的耦合性 const methodExtends = () => { const MethodDecorator = (target, propertyKey, descriptor) => { // 获取方法本身 const method = descriptor.value; // 对该方法的生命周期进行操作 descriptor.value = (...args) => { console.log('MethodDecorator before run'); const data = method.call(this, args); console.log('MethodDecorator after run'); return data; }; }; class Test { method() { return ';;;;'; } } __decorate([ MethodDecorator ], Test.prototype, "method", null); console.log(new Test().method()); }; methodExtends(); // 通过装饰入参的形式对方法进行扩展,使用参数可以对业务进行更强的定制化处理 const methodExtendsByParams = () => { const MethodDecorator = (param) => { console.log('methodExtendsByParams', param); return (target, propertyKey, descriptor) => { // 获取方法本身 const method = descriptor.value; // 对该方法的生命周期进行操作 descriptor.value = (...args) => { console.log('before run'); const data = method.call(this, args); console.log('after run'); return data; }; }; }; class Test { method() { return ';;;;'; } } __decorate([ MethodDecorator('user') ], Test.prototype, "method", null); console.log(new Test().method()); }; methodExtendsByParams();
复制
方法参数装饰器
typescript
// 通过装饰器对方法中的属性进行扩展 /** * target 实例自身 * methodName 方法名 * paramIndex 参数的下标位置 */ const methodParamExtends = () => { const methodPramDecorator = (param: string) => { return (target: any, methodName: string, paramIndex: any) => { console.log('target', target, methodName, paramIndex); target.decoratorData = '222'; } }; class Test { decoratorData!: string; init (@methodPramDecorator('agent') type: string) { return type; } } const test = new Test(); const data = test.init('20230611'); console.log(data, test.decoratorData); return data; }; methodParamExtends();
复制
javascript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; // 通过装饰器对方法中的属性进行扩展 /** * target 实例自身 * methodName 方法名 * paramIndex 参数的下标位置 */ const methodParamExtends = () => { const methodPramDecorator = (param) => { return (target, methodName, paramIndex) => { console.log('target', target, methodName, paramIndex); target.decoratorData = '222'; }; }; class Test { init(type) { return type; } } __decorate([ __param(0, methodPramDecorator('agent')) ], Test.prototype, "init", null); const test = new Test(); const data = test.init('20230611'); console.log(data, test.decoratorData); return data; }; methodParamExtends();
复制
寄存器装饰器
typescript
// 通过装饰器对寄存器进行扩展 /** * target 实例自身 * propertyKey 属性key * descriptor 属性描述符 */ const setgetExtends = () => { const SetGetDecorator = (param: string) => { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { const set = descriptor.set; const get = descriptor.get; descriptor.set = function (value) { console.log('GetDecorator before run', value); if (typeof value === 'string') { value += ` set decrator ${param}`; } set.call(this, value); console.log('GetDecorator after run', value); }; descriptor.get = function () { console.log('GetDecorator before run', target); let data = get.call(this); console.log('GetDecorator after run'); if (typeof data === 'string') { data += ` get decrator ${param}`; } return data; } } }; class Test { #name: string; @SetGetDecorator('custom setget') set name (name: string) { this.#name = name; } get name () { return this.#name; } } const user = new Test(); user.name = 'user'; console.log('setgetExtends', user.name); }; setgetExtends();
复制
javascript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to set private field on non-instance"); } privateMap.set(receiver, value); return value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); }; // 通过装饰器对寄存器进行扩展 /** * target 实例自身 * propertyKey 属性key * descriptor 属性描述符 */ const setgetExtends = () => { var _name; const SetGetDecorator = (param) => { return (target, propertyKey, descriptor) => { const set = descriptor.set; const get = descriptor.get; descriptor.set = function (value) { console.log('GetDecorator before run', value); if (typeof value === 'string') { value += ` set decrator ${param}`; } set.call(this, value); console.log('GetDecorator after run', value); }; descriptor.get = function () { console.log('GetDecorator before run', target); let data = get.call(this); console.log('GetDecorator after run'); if (typeof data === 'string') { data += ` get decrator ${param}`; } return data; }; }; }; class Test { constructor() { _name.set(this, void 0); } set name(name) { __classPrivateFieldSet(this, _name, name); } get name() { return __classPrivateFieldGet(this, _name); } } _name = new WeakMap(); __decorate([ SetGetDecorator('custom setget') ], Test.prototype, "name", null); const user = new Test(); user.name = 'user'; console.log('setgetExtends', user.name); }; setgetExtends();
复制
属性装饰器
typescript
// 通过属性装饰器对属性进行扩展 const paramExtends = () => { const ParamDecorator = (param: string) => { return function (target: any, key: any) { target[key] = param; console.log(`init param ${key} to ${param}`); } } class Test { @ParamDecorator('www.baidu.com') private url!: string; getName () { return this.url; } } const test = new Test(); const data = test.getName(); console.log('data', data); }; paramExtends();
复制
javascript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; // 通过属性装饰器对属性进行扩展 const paramExtends = () => { const ParamDecorator = (param) => { return function (target, key) { target[key] = param; console.log(`init param ${key} to ${param}`); }; }; class Test { getName() { return this.url; } } __decorate([ ParamDecorator('www.baidu.com') ], Test.prototype, "url", void 0); const test = new Test(); const data = test.getName(); console.log('data', data); }; paramExtends();
复制
装饰器执行顺序
示例代码
typescript
// 不同装饰器的执行顺序 const CustomClassDecorator = (constructor: Function) => { console.log('类装饰器'); }; const CustomMethodDecorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { console.log('方法装饰器'); }; const CustomMethodParamDecorator = (target: any, methodName: string, paramIndex: any) => { console.log('方法参数装饰器'); } const CustomParamDecorator = (target: any, key: any) => { console.log(`参数装饰器`); } const CustomSetGetDecorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { console.log('寄存器装饰器'); } @CustomClassDecorator class Test { @CustomParamDecorator sex!: string; #name !: string; @CustomSetGetDecorator set name (name: string) { this.#name = name; } get name () { return this.#name } @CustomMethodDecorator handleName (@CustomMethodParamDecorator prefix: string) { return prefix + this.name; } } const instance = new Test(); const data = instance.handleName('prefix'); console.log(data);
复制
javascript
// 不同装饰器的执行顺序 var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to set private field on non-instance"); } privateMap.set(receiver, value); return value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return privateMap.get(receiver); }; var _name; const CustomClassDecorator = (constructor) => { console.log('类装饰器'); }; const CustomMethodDecorator = (target, propertyKey, descriptor) => { console.log('方法装饰器'); }; const CustomMethodParamDecorator = (target, methodName, paramIndex) => { console.log('方法参数装饰器'); }; const CustomParamDecorator = (target, key) => { console.log(`参数装饰器`); }; const CustomSetGetDecorator = (target, propertyKey, descriptor) => { console.log('寄存器装饰器'); }; let Test = class Test { constructor() { _name.set(this, void 0); } set name(name) { __classPrivateFieldSet(this, _name, name); } get name() { return __classPrivateFieldGet(this, _name); } handleName(prefix) { return prefix + this.name; } }; _name = new WeakMap(); __decorate([ CustomParamDecorator ], Test.prototype, "sex", void 0); __decorate([ CustomSetGetDecorator ], Test.prototype, "name", null); __decorate([ CustomMethodDecorator, __param(0, CustomMethodParamDecorator) ], Test.prototype, "handleName", null); Test = __decorate([ CustomClassDecorator ], Test); const instance = new Test(); const data = instance.handleName('prefix'); console.log(data);
复制
执行结果
参数装饰器 寄存器装饰器 方法参数装饰器 方法装饰器 类装饰器 prefixundefined
复制
装饰器原理
以下代码是重写过的,方便理解和回顾。由于Reflect.decorate装饰器反射机制还不支持,且相关资料较少,所以在本文中不进行深入研究。
/** * Test = __decorate([ClassDecrator], Test) * decorators 装饰器列表 * target 类实例 * key 属性名称 * desc 变量属性描述符 */ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { /** * 获取请求参数,咱本示例中,请求参数为2 * Test = __decorate([ClassDecrator], Test) */ var c = arguments.length // 初始化r变量 var r = null; // 如果请求参数小于三个,在本示例中满足 if (c < 3) { // 将类实例赋予r,也就是将Test赋给r r = target; } else { // 如果属性描述符为空 if (desc === null) { // 返回指定属性名的描述符 desc = Object.getOwnPropertyDescriptor(target, key); r = desc; } else { // 如果存在描述符,则直接赋予r r = desc; } } // 由此可见,在类装饰器下,r为类实例本身,在方法等装饰器下,r为属性描述符 var d; // 是否支持es6的Reflect,暂时跳过,文章后面单独会将 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") { r = Reflect.decorate(decorators, target, key, desc) } // 如果不支持es6的Reflect,则向下执行 else { // 在这里倒叙循环执行每一个装饰器,由此看出ts装饰器的执行顺序 for (var i = decorators.length - 1; i >= 0; i--) { d = decorators[i]; if (d) { var temp = null; if (c < 3) { temp = d(r); } else { if (c > 3) { temp = d(target, key, r); } else { temp = d(target, key); } } if (temp) { r = temp; } } } } // 如果参数大于3个,则将属性名和属性描述符赋予该实例 if (c > 3 && r) { Object.defineProperty(target, key, r); } // 返回该实例实例或属性描述符 return r; };
复制