(미래를 예측하는 가장 좋은 방법은 그것을 발명하는 것이다 - 네그로폰테)
데코레이터
데코레이터는 @ 형식을 사용하여 속성, 레지스터, 메서드, 메서드 매개 변수 및 클래스에 주입되는 보다 현대적인 코드 패턴입니다.예를 들어 데코레이터는 Angular, Nestjs 및 미드웨이와 같은 인기 있는 프레임워크에서도 사용됩니다. 데코레이터에 대한 자세한 설명을 위한
공식 지침
tc39 제안
tc39 제안은 5단계로 나뉩니다.
Stage 0 - Idea (Strawman): Babel 플러그인을 사용하는 아이디어일 뿐입니다.
1단계 - 제안: 후속 조치를 취할 가치가 있습니다.
2단계 - 초안: 초기 사양.
3단계 - 후보: 사양을 완성하고 브라우저에서 초기에 구현합니다.
4단계 - 완료: 다음 연간 릴리스에 추가됩니다.
데코레이터는 현재 3단계에 있으며 가까운 장래에 js에서도 데코레이터가 지원될 것이라고 생각합니다. 데코레이터는 TypeScript 5.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"
]
}
클래스 데코레이터
타자기
// 通过装饰器对类进行扩展,减少对代码侵入性和业务间的耦合性
// 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();
자바스크립트
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();
메서드 데코레이터
타자기
/**
* 入参解释
* 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();
자바스크립트
/**
* 入参解释
* 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();
메서드 매개변수 데코레이터
타자기
// 通过装饰器对方法中的属性进行扩展
/**
* 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();
자바스크립트
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();
데코레이터 등록
타자기
// 通过装饰器对寄存器进行扩展
/**
* 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();
자바스크립트
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();
속성 데코레이터
타자기
// 通过属性装饰器对属性进行扩展
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();
자바스크립트
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();
데코레이터 실행 순서
샘플 코드
타자기
// 不同装饰器的执行顺序
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);
자바스크립트
// 不同装饰器的执行顺序
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;
};