(预测未来最好的方法就是把它创造出来——尼葛洛庞帝)
装饰器
装饰器一种更现代的代码模式,通过使用@的形式注入在属性,寄存器,方法,方法参数和类中,比如在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;
};