(預測未來最好的方法就是把它創(chuàng)造出來——尼葛洛龐帝)
裝飾器
裝飾器一種更現(xiàn)代的代碼模式,通過使用@的形式注入在屬性,寄存器,方法,方法參數(shù)和類中,比如在Angular,Nestjs和midway等流行框架中也都用到了裝飾器。
官方說明
對裝飾器更詳細的相關(guān)說明
tc39提案
tc39提案一共分為五個階段
Stage 0 - 設(shè)想(Strawman):只是一個想法,可能有 Babel插件。
Stage 1 - 建議(Proposal):這是值得跟進的。
Stage 2 - 草案(Draft):初始規(guī)范。
Stage 3 - 候選(Candidate):完成規(guī)范并在瀏覽器上初步實現(xiàn)。
Stage 4 - 完成(Finished):將添加到下一個年度版本發(fā)布中。
裝飾器目前已經(jīng)在階段3,相信不久的將來,js中也會支持裝飾器。在typescript5.0中已經(jīng)完全支持了裝飾器。
該提案進度更詳細的相關(guān)說明
裝飾器實踐
tsconfig.json
{
"compilerOptions": {
"target": "ES6", // 為發(fā)出的JavaScript設(shè)置JavaScript語言版本,并包含兼容的庫聲明
"experimentalDecorators": true, // 啟用對遺留實驗裝飾器的實驗支持
"module": "ES6", // 指定生成的模塊代碼
"esModuleInterop": true, // 發(fā)出額外的JavaScript以簡化對導入CommonJS模塊的支持。這為類型兼容性啟用了“allowSyntheticDefaultImports”
"moduleResolution": "node", // 指定TypeScript如何從給定的模塊說明符中查找文件
"outDir": "dist", // 為所有發(fā)出的文件指定輸出文件夾
"rootDir": "src", // 指定源文件中的根文件夾
},
"include": [ // 需要編譯的目錄文件
"src/**/*",
],
"exclude": [ // 需要排除的目錄文件
"node_modules"
]
}
官方更詳細的配置說明
類裝飾器
typescript
// 通過裝飾器對類進行擴展,減少對代碼侵入性和業(yè)務(wù)間的耦合性
// constructor參數(shù)為類的構(gòu)造函數(shù)
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();
// 通過裝飾器入?yún)⒌男问綄︻愡M行擴展,使用參數(shù)可以對業(yè)務(wù)進行更強的定制化處理
const classExtendByParams = () => {
const ClassDecorator = (param: string) => {
return function (constructor: Function) {
// 針對入?yún)⒆霾煌奶幚?/span>
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;
};
// 通過裝飾器對類進行擴展,減少對代碼侵入性和業(yè)務(wù)間的耦合性
// constructor參數(shù)為類的構(gòu)造函數(shù)
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();
// 通過裝飾器入?yún)⒌男问綄︻愡M行擴展,使用參數(shù)可以對業(yè)務(wù)進行更強的定制化處理
const classExtendByParams = () => {
const ClassDecorator = (param) => {
return function (constructor) {
// 針對入?yún)⒆霾煌奶幚?/span>
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
/**
* 入?yún)⒔忉? * target: 對于靜態(tài)成員來說是類的構(gòu)造函數(shù),對于實例成員來說是類的原型對象
* propertyKey: 屬性的名稱
* descriptor: 屬性的描述器
*/
/**
* PropertyDescriptor參數(shù)解釋
* PropertyDescriptor的參數(shù)各項為js的屬性描述符,在創(chuàng)建變量或方法等對象時,js會默認賦予這些描述符
* 詳細的閱讀https://www.tektutorialshub.com/javascript/javascript-property-descriptors-enumerable-writable-configurable/
* descriptor 參數(shù)
* value 方法自身
* writable 該方法是否可以被變更
* enumerable 是否可以被枚舉
* configurable 決定是否可配置,如果為false,則value,writable,enumerable不能被修改
*/
// 通過裝飾器對方法進行擴展,減少對代碼侵入性和業(yè)務(wù)間的耦合性
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();
// 通過裝飾入?yún)⒌男问綄Ψ椒ㄟM行擴展,使用參數(shù)可以對業(yè)務(wù)進行更強的定制化處理
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
/**
* 入?yún)⒔忉? * target: 對于靜態(tài)成員來說是類的構(gòu)造函數(shù),對于實例成員來說是類的原型對象
* propertyKey: 屬性的名稱
* descriptor: 屬性的描述器
*/
/**
* PropertyDescriptor參數(shù)解釋
* PropertyDescriptor的參數(shù)各項為js的屬性描述符,在創(chuàng)建變量或方法等對象時,js會默認賦予這些描述符
* 詳細的閱讀https://www.tektutorialshub.com/javascript/javascript-property-descriptors-enumerable-writable-configurable/
* descriptor 參數(shù)
* 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;
};
// 通過裝飾器對方法進行擴展,減少對代碼侵入性和業(yè)務(wù)間的耦合性
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();
// 通過裝飾入?yún)⒌男问綄Ψ椒ㄟM行擴展,使用參數(shù)可以對業(yè)務(wù)進行更強的定制化處理
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();
方法參數(shù)裝飾器
typescript
// 通過裝飾器對方法中的屬性進行擴展
/**
* target 實例自身
* methodName 方法名
* paramIndex 參數(shù)的下標位置
*/
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 參數(shù)的下標位置
*/
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();
裝飾器執(zhí)行順序
示例代碼
typescript
// 不同裝飾器的執(zhí)行順序
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('方法參數(shù)裝飾器');
}
const CustomParamDecorator = (target: any, key: any) => {
console.log(`參數(shù)裝飾器`);
}
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文章來源:http://www.zghlxwxcb.cn/news/detail-483850.html
// 不同裝飾器的執(zhí)行順序
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('方法參數(shù)裝飾器');
};
const CustomParamDecorator = (target, key) => {
console.log(`參數(shù)裝飾器`);
};
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);
執(zhí)行結(jié)果
參數(shù)裝飾器
寄存器裝飾器
方法參數(shù)裝飾器
方法裝飾器
類裝飾器
prefixundefined
裝飾器原理
以下代碼是重寫過的,方便理解和回顧。由于Reflect.decorate裝飾器反射機制還不支持,且相關(guān)資料較少,所以在本文中不進行深入研究。文章來源地址http://www.zghlxwxcb.cn/news/detail-483850.html
/**
* Test = __decorate([ClassDecrator], Test)
* decorators 裝飾器列表
* target 類實例
* key 屬性名稱
* desc 變量屬性描述符
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
/**
* 獲取請求參數(shù),咱本示例中,請求參數(shù)為2
* Test = __decorate([ClassDecrator], Test)
*/
var c = arguments.length
// 初始化r變量
var r = null;
// 如果請求參數(shù)小于三個,在本示例中滿足
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,則向下執(zhí)行
else {
// 在這里倒敘循環(huán)執(zhí)行每一個裝飾器,由此看出ts裝飾器的執(zhí)行順序
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;
}
}
}
}
// 如果參數(shù)大于3個,則將屬性名和屬性描述符賦予該實例
if (c > 3 && r) {
Object.defineProperty(target, key, r);
}
// 返回該實例實例或?qū)傩悦枋龇?/span>
return r;
};
到了這里,關(guān)于TypeScript 自定義裝飾器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!