1. 装饰器是什么
- 它是⼀个表达式
- 该表达式被执⾏后,返回⼀个函数
- 函数的⼊参分别为 target、name 和 descriptor
- 执⾏该函数后,可能返回 descriptor 对象,⽤于配置 target 对象
装饰器就是一个方法或者叫函数,可以注入(写到)到类、方法、属性、参数,对象上,扩展其功能。
课外了解:高阶组件本质上也采用了装饰器的思想
装饰器要解决的问题:
装饰器就是解决在不修改原来类、方法,属性,参数的时候为其添加额外的功能。比如:为整个项目的所有业务类【假如50个类】的所有方法【假如6个方法】都增加日志信息,如果一个一个的增加,那要增加300次日志调用语句,假如日后日志文件格式发生了改变,也还需要修改300次。 如果有了装饰器,只需要修改一次就可以。
有了依赖注入,能大大降低项目的耦合度,大大提升项目的可扩展性。
使用和创建分离是依赖注入的核心思想。
2. 装饰器的分类
- 类装饰器(Class decorators)
- 属性装饰器(Property decorators)
- ⽅法装饰器(Method decorators)
- 参数装饰器(Parameter decorators)
执行顺序:属性装饰器-->第一个方法参数装饰器-->第一个方法装饰器-->......-->构造器参数装饰器-->类装饰器
3. 类装饰器
接收⼀个参数:
target: TFunction - 被装饰的类
1. 不带参数的类装饰器
function FirstClassDecorator(targetClass: any) {
Object.keys(targetClass.prototype).forEach((methodname) => {
console.log("方法", methodname)
let dataprop = Object.getOwnPropertyDescriptor(targetClass.prototype, methodname)
console.log("方法数据属性:", dataprop);
})
}
@FirstClassDecorator
class CustomerService {
name: string = "下单"
constructor() {}
buy() {
console.log(this.name + "购买");
}
placeOrder() {
console.log(this.name + "下单购买");
}
}
2. 带参数类装饰器
function FirstClassDecorator(classinfo: string) {
return function (targetClass: any) {
Object.keys(targetClass.prototype).forEach((methodname) => {
console.log("方法", methodname)
let dataprop = Object.getOwnPropertyDescriptor(targetClass.prototype, methodname)
console.log("方法数据属性:", dataprop);
})
}
}
3. 底层实现
"use strict";
var __decorate = (this && this.__decorate) ||
function (decorators, target, key, desc) {
// argsnum 参数个数
var argsnum = arguments.length;
console.log("argsnum:", argsnum)
// targetinfo 被装饰器修饰的目标【本案例为类】
// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
console.log("targetinfo:", targetinfo);
// decorator保存装饰器数组元素
var decorator;
// 元数据信息,支持reflect-metadata元数据
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
targetinfo = Reflect.decorate(decorators, target, key, desc);
} else
// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
for (var i = decorators.length - 1; i >= 0; i--) {
if (decorator = decorators[i]) {
// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)
// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
console.log("decorator(targetinfo):", decorator(targetinfo))
targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
console.log("targetinforesult:", targetinfo)
}
}
return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层JS 组合装饰器和目标类 __decorate函数结束
// 不带参数的装饰器
function FirstClassDecorator (targetClass) {
var targetClassObj = new targetClass();
targetClassObj.buy();
console.log("targetClass.name:", targetClass.name);
}
Object.defineProperty(exports, "__esModule", { value: true });
// 带参数的装饰器
function FirstClassDecorator (params) {
console.log("params:", params);
return function (targetClass) {
var targetClassObj = new targetClass();
targetClassObj.buy();
};
}
var CustomerService = /** @class */ (function () {
function CustomerService () {
this.name = "下单";
}
CustomerService.prototype.buy = function () {
console.log(this.name + "购买");
};
CustomerService.prototype.placeOrder = function () {
console.log(this.name + "下单购买");
};
CustomerService = __decorate([
FirstClassDecorator("我是用来修饰CustomerService类的装饰器参数")
], CustomerService);
return CustomerService;
}());
4. 泛型工厂类继承装饰器
1. demo
function ClassFunctionExtends<T extends { new (...args: any[]): any }> (mytargetClass: T) {
console.log("mytargetClass", mytargetClass);
class SonClass extends mytargetClass {
constructor(...args: any[]) {
super(args);
console.log("SonClass执行结束");
}
commonMethod() {
//console.log("this:", this)
console.log("name:", this.name)
}
}
return SonClass
}
// 2. 目标类
@ClassFunctionExtends
class Test {
name!: string;
age!: number
// 1.先执行原来构造函数
constructor(name: string) {
this.name = name;
}
eat() {
console.log(this.name, "吃饭");
}
}
//let SonClass = ClassFunctionExtends<typeof Test>(Test)
let SonClass = ClassFunctionExtends(Test)
let SonClassObj=new SonClass("wangwu");
2. 实现
// 继承
let __extends = (function (Son, Parent) {
function getStaticExtendsWithForIn (Son, Parent) {
for (let key in Parent) {
if (Object.prototype.hasOwnProperty.call(Parent, key)) {
Son[key] = Parent[key]
}
}
}
function getStaticExtendsWithObjectkeys (Son, Parent) {
Object.keys(Parent).forEach((key) => {
Son[key] = Parent[key]
})
}
function getStaticExtendsWithProto (Son, Parent) {
Son.__proto__ = Parent;
}
let MyextendStatics = function (Son, Parent) {
let MyextendStatics = Object.setPrototypeOf || getStaticExtendsWithForIn ||
getStaticExtendsWithObjectkeys || getStaticExtendsWithProto
return MyextendStatics(Son, Parent)
}
return function (Son, Parent) {
MyextendStatics(Son, Parent)
function Middle () {
this.constructor = Son;
}
if (Parent) {//如果不为空 如果父类存在
Middle.prototype = Parent.prototype;
Son.prototype = new Middle()
} else {// 如果父类不存在
Son.prototype = Object.create(null)
}
console.log("Object.create(null):", Object.create(null));
}
}())
// 2. 底层JS 组合装饰器和目标类 __decorate 函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
// argsnum 参数个数
var argsnum = arguments.length;
// targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
// decorator保存装饰器数组元素
var decorator;
// 元数据信息,支持reflect-metadata元数据
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
targetinfo = Reflect.decorate(decorators, target, key, desc);
} else
// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
for (var i = decorators.length - 1; i >= 0; i--) {
if (decorator = decorators[i]) {
// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)
// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
console.log("targetinforesult:", targetinfo)
}
}
return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 2.装饰器类
function ClassFunctionExtends (mytargetClass) {
console.log("mytargetClass", mytargetClass);
var SonClass = /** @class */ (function (_super) {
__extends(SonClass, _super);
function SonClass () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var _this = _super.call(this, args) || this;
console.log("SonClass执行结束");
return _this;
}
SonClass.prototype.commonMethod = function () {
//console.log("this:", this)
console.log("name:", this.name);
};
return SonClass;
}(mytargetClass));
return SonClass;
}
// 目标类
var Test = /** @class */ (function () {
// 1.先执行原来构造函数
function Test (name) {
this.name = name;
console.log("执行");
//console.log("beforendame:", this.name)// lisi
}
Test.prototype.eat = function () {
console.log(this.name, "吃饭");
};
Test = __decorate([
ClassFunctionExtends
], Test);
return Test;
}());
// 4.测试
var test = new Test("ok");
// let SonClass = twoDecorator<typeof Test>(Test)
//let SonClass = twoDecorator(Test)
//let SonClassObj=new SonClass("wangwu");
5. 方法装饰器
接收三个参数:
target: Object - 被装饰的类
propertyKey: string | symbol - ⽅法名
descriptor: TypePropertyDescript - 属性描述符
1. demo
// 1 不带参数的方法装饰器
function MyMethodDecorator(targetClassPrototype: any,key: string, methodDecri: PropertyDescriptor) {
console.log("targetClassPrototype:", targetClassPrototype)//
console.log("key:", key);
}
class RoleService {
public roleName: string = "管理员"
constructor() {
}
@MyMethodDecorator
DistribRoles() {// 分配角色
console.log("分配角色.....");
}
}
export { }
2. 常见场景
在方法装饰器中 拦截目标类的方法, 可以壮大或修改目标类的方法的功能
比如:增加一个日志信息,修改方法参数进行功能扩展处理。
// 增强目标类的方法功能
class StringUtil {//工具类
public static trimSpace(str: string): string {
return str.replace(/\s+/g, "")
}
}
class RoleService {
public roleName: string = "管理员"
constructor() {
}
@MethodInterceptor("DistribRoles方法")
DistribRoles(userName: string, isValid: boolean) {// 分配角色
console.log("分配角色.....");
}
}
function MethodInterceptor(paramsValue: any) {
console.log("方法装饰器....");
return function (targetClassPrototype: any, methodName: any,
methodDecri: PropertyDescriptor) {
//targetMethodSave.value 表示原来目标类HttpClient的show()方法
// 1.1 先保存目标类的方法到targetMethodSave
console.log("进入方法装饰器:methodDecri:", methodDecri);
let targetMethodSave = methodDecri.value;
console.log("targetMethodSave:", targetMethodSave);
// 1.2.让value函数建立新得函数对象空间
// value建立一个新的函数后,
// RoleService对象调用DistribRoles;会执行value指向的新函数
// 并不会执行原来RoleService目标类中DistribRoles方法
// 这里建立的一个新函数
methodDecri.value = function (...args: any[]) {
console.log("this:", this);
// 迭代所有参数
args = args.map((arg) => {
if (typeof arg === "string") {
return StringUtil.trimSpace(arg);
}
return arg;
})
console.log(args)
// 1.4.总结:这是一种典型的用方法装饰器扩大原来方法功能的案例
// 1.5 但如果增强原来方法功能后,还想继续执行原来RoleService类中DistribRoles方法
// 使用apply执行targetMethodSave原来函数
targetMethodSave.apply(this, args)
}
// 方法执行之后,继续执行后续代码
console.log("methodDecri.value:");
}
}
3. 源码
"use strict";
// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
// argsnum 参数个数
var argsnum = arguments.length;
// targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
// decorator保存装饰器数组元素
var decorator;
// 元数据信息,支持reflect-metadata元数据
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
targetinfo = Reflect.decorate(decorators, target, key, desc);
} else
// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
for (var i = decorators.length - 1; i >= 0; i--) {
if (decorator = decorators[i]) {
// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)
// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
console.log("targetinforesult:", targetinfo)
}
}
return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
Object.defineProperty(exports, "__esModule", { value: true });
// 底层JS 组合装饰器和目标类 __decorate函数结束
// 2. 工具类
var StringUtil = /** @class */ (function () {
function StringUtil () {
}
StringUtil.trimSpace = function (str) {
return str.replace(/\s+/g, "");
};
return StringUtil;
}());
// 目标类
var RoleService = /** @class */ (function () {
function RoleService () {
this.roleName = "管理员";
}
RoleService.prototype.DistribRoles = function (userName, isValid) {
console.log("分配角色.....");
};
__decorate([
MethodInterceptor("DistribRoles方法")
], RoleService.prototype, "DistribRoles", null);
return RoleService;
}());
// 3. 装饰器方法
function MethodInterceptor (paramsValue) {
console.log("方法装饰器....");
return function (targetClassPrototype, methodName, methodDecri) {
//targetMethodSave.value 表示原来目标类HttpClient的show()方法
// 1.1 先保存目标类的方法到targetMethodSave
console.log("进入方法装饰器:methodDecri:", methodDecri);
var targetMethodSave = methodDecri.value;
console.log("targetMethodSave:", targetMethodSave);
// 1.2.让value函数建立新得函数对象空间
// value建立一个新的函数后,
// RoleService对象调用DistribRoles;会执行value指向的新函数
// 并不会执行原来RoleService目标类中DistribRoles方法
// 这里建立的一个新函数就和后端 Java的spring AOP中的方法拦截器思想就完全一样
methodDecri.value = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
console.log("this:", this);
// 迭代所有参数
args = args.map(function (arg) {
if (typeof arg === "string") {
return StringUtil.trimSpace(arg);
}
return arg;
});
console.log(args);
// 1.4.总结:这是一种典型的用方法装饰器扩大原来方法功能的案例
// 1.5 但如果增强原来方法功能后,还想继续执行原来RoleService类中DistribRoles方法
// 使用apply执行targetMethodSave原来函数
targetMethodSave.apply(this, args);
};
// 方法执行之后,继续执行后续代码
console.log("methodDecri.value:");
};
}
6. 属性装饰器
接收两个参数:
target: Object - 被装饰的类
propertyKey: string | symbol - 被装饰类的属性名
1. demo
// 属性装饰器【这里先学会使用,后面实战再深入】
function loginProperty(attrValue: any) {
return function (targetclassPrototype: object, attrname: string | symbol) {
console.log("targetclassPrototype:", targetclassPrototype);
console.log("attrname:", attrname);
(targetclassPrototype.constructor as any).custLevelDescri = function () {
console.log("消费5000元升级为贵宾");
console.log("消费10000元升级为贵宾,赠送微波炉一个");
}
}
}
// 顾客目标类
class CustomerService {
public custname: string = "王五"
@loginProperty("顾客登记")
public degree!: string
constructor() {
}
show() {
console.log("顾客名:", this.custname)
}
}
(CustomerService as any).custLevelDescri()
2. 源码
// 1. 底层JS 组合装饰器和目标类 __decorate 函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
// argsnum 参数个数
var argsnum = arguments.length;
// targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
// decorator保存装饰器数组元素
var decorator;
// 元数据信息,支持reflect-metadata元数据
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
targetinfo = Reflect.decorate(decorators, target, key, desc);
} else
// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
for (var i = decorators.length - 1; i >= 0; i--) {
if (decorator = decorators[i]) {
// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)
// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
console.log("targetinforesult:", targetinfo)
}
}
return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层 JS 组合装饰器和目标类 __decorate 函数结束
// 2. 属性装饰器
function loginProperty(attrValue) {
return function (targetclassPrototype, attrname) {
console.log("targetclassPrototype:", targetclassPrototype);
console.log("attrname:", attrname);
targetclassPrototype.constructor.custLevelDescri = function () {
console.log("消费5000元升级为贵宾");
console.log("消费10000元升级为贵宾,赠送微波炉一个");
};
};
}
// 3.目标类
var CustomerService = /** @class */ (function () {
function CustomerService() {
this.custname = "王五";
}
CustomerService.prototype.show = function () {
console.log("顾客名:", this.custname);
};
__decorate([
loginProperty("顾客登记")
], CustomerService.prototype, "degree");
return CustomerService;
}());
CustomerService.custLevelDescri();
7. 参数装饰器
接收三个参数:
target: Object - 被装饰的类
propertyKey: string | symbol - ⽅法名
parameterIndex: number - ⽅法中参数的索引值
1. demo
function UrlParam(params: any) {
return function paramDecorator(targetClassPrototype: any,
methodname: string, paramindex: number) {
console.log("targetClassPrototype:", targetClassPrototype)
console.log("methodname:", methodname);
console.log("paramindex:", paramindex);
targetClassPrototype.info = params
}
}
class People {
eat(@UrlParam("地址信息") address: string, who: string) {
console.log("address:", address);
}
}
2. 源码
"use strict";
// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
// argsnum 参数个数
var argsnum = arguments.length;
// targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
// decorator保存装饰器数组元素
var decorator;
// 元数据信息,支持reflect-metadata元数据
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
targetinfo = Reflect.decorate(decorators, target, key, desc);
} else
// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
for (var i = decorators.length - 1; i >= 0; i--) {
if (decorator = decorators[i]) {
// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)
// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
console.log("targetinforesult:", targetinfo)
}
}
return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层JS 组合装饰器和目标类 __decorate函数结束
// Object.defineProperty(exports, "__esModule", { value: true });
// 2 带参数的装饰器
function UrlParam(params) {
return function paramDecorator(targetClassPrototype, methodname, paramindex) {
console.log("targetClassPrototype:", targetClassPrototype);
console.log("methodname:", methodname);
console.log("paramindex:", paramindex);
targetClassPrototype.info = params;
};
}
// 3 封装带参数装饰器
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var People = /** @class */ (function () {
function People() {
}
People.prototype.eat = function (address, who) {
console.log("address:", address);
};
__decorate([
__param(0, UrlParam("地址信息"))
], People.prototype, "eat", null);
return People;
}());
8. 装饰器执行顺序
属性装饰器-->第一个方法参数装饰器-->第一个方法装饰器-->......-->构造器参数装饰器-->类装饰器
1. demo
// 1. 类丶属性丶方法 丶参数装饰器组合案例实现 2. 执行顺序
function firstMethodDecorator(targetClassPrototype: any,
methodname: string) {
console.log("=============执行第一个方法装饰器==============")
console.log("类名:", targetClassPrototype)// 类原型对象变量 URLInfo { show: [Function] }
console.log("方法名:", methodname);//key
}
function secondMethodDecorator(params: string) {
return function (targetClassPrototype: any, methodname: string) {
console.log("=============执行第二个方法装饰器==============")
console.log("类名:", targetClassPrototype)// 类原型对象变量 URLInfo { show: [Function] }
console.log("方法名:", methodname);//key
}
}
function paramDecorator(targetClassPrototype: any, paramname: string, paramindex: number) {
console.log("=============执行参数装饰器==============")
console.log("targetClassPrototype:", targetClassPrototype);
console.log("参数名:", paramname);
console.log("参数索引:", paramindex);
}
function UrlPropDecorator(targetClassPrototype: any, attrname: any) {
console.log("=============执行属性装饰器==============")
console.log("targetClassPrototype:", targetClassPrototype);
console.log("属性名:", attrname);
}
function URLInfoDecorator(targetClassPrototype: any) {
console.log("==========类装饰器============")
console.log("targetClassPrototype:", targetClassPrototype);
}
function constructorDecorator(params: any) {
return function (targetClassPrototype: any, paramname: string, paramindex: number) {
console.log("==========构造器参数装饰器============")
console.log("构造器参数装饰器", targetClassPrototype);
console.log("构造器参数名为:", paramname);
console.log("构造器参数索引位置:", paramindex);
}
}
@URLInfoDecorator
class URLInfo {
constructor(@constructorDecorator("url") public uri: string) {
}
@UrlPropDecorator
public url: string = "https://www.imooc.com"
@firstMethodDecorator
methodOne(@paramDecorator data: string) {
console.log("this:", this);
console.log("目标类:", this.uri)
}
@secondMethodDecorator("yes")
methodTwo(@paramDecorator address: string) {
console.log(address)
}
}
export { }
2. 源码
// 1. 底层JS 组合装饰器和目标类 __decorate函数
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
// argsnum 参数个数
var argsnum = arguments.length;
// targetinfo 被装饰器修饰的目标【类或属性或方法或方法参数,本案例为类】
// argsnum=2 装饰器修饰的是类或者构造器参数,targetinfo=target[类名]
// argsnum=4 装饰器修饰的是方法【第四个参数desc等于null] targetinfo=该方法的数据属性【desc = Object.getOwnPropertyDescriptor(target, key) 】
// argsnum=3 装饰器修饰的是方法参数或者属性,targetinfo=undefined
var targetinfo = argsnum < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc;//S100
// decorator保存装饰器数组元素
var decorator;
// 元数据信息,支持reflect-metadata元数据
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
targetinfo = Reflect.decorate(decorators, target, key, desc);
} else
// 装饰器循环,倒着循环,说明同一个目标上有多个装饰器,执行顺序是倒着执行
for (var i = decorators.length - 1; i >= 0; i--) {
if (decorator = decorators[i]) {
// 如果参数小于3【decorator为类装饰器或者构造器参数装饰器】执行decorator(targetinfo)直接执行decorator装饰器,并传递目标targetinfo,这里是类
// 如果参数大于3【decorator为方法装饰器】 直接执行 decorator(target, key, targetinfo)
// 如果参数等于3 【decorator为方法参数装饰器或者属性装饰器】 直接执行decorator(target, key)
// targetinfo最终为各个装饰器执行后的返回值,但如果没有返回值,直接返回第S100行的targetinfo
targetinfo = (argsnum < 3 ? decorator(targetinfo) : argsnum > 3 ?
decorator(target, key, targetinfo) : decorator(target, key)) || targetinfo;
console.log("targetinforesult:", targetinfo)
}
}
return argsnum > 3 && targetinfo && Object.defineProperty(target, key, targetinfo), targetinfo;
}
// 底层JS 组合装饰器和目标类 __decorate函数结束
function firstMethodDecorator(targetClassPrototype, methodname) {
console.log("=============执行第一个方法装饰器==============");
console.log("类名:", targetClassPrototype); // 类原型对象变量 URLInfo { show: [Function] }
console.log("方法名:", methodname); //key
}
function secondMethodDecorator(params) {
return function (targetClassPrototype, methodname) {
console.log("=============执行第二个方法装饰器==============");
console.log("类名:", targetClassPrototype); // 类原型对象变量 URLInfo { show: [Function] }
console.log("方法名:", methodname); //key
};
}
function paramDecorator(targetClassPrototype, paramname, paramindex) {
console.log("=============执行参数装饰器==============");
console.log("targetClassPrototype:", targetClassPrototype);
console.log("参数名:", paramname);
console.log("参数索引:", paramindex);
}
function UrlPropDecorator(targetClassPrototype, attrname) {
console.log("=============执行属性装饰器==============");
console.log("targetClassPrototype:", targetClassPrototype);
console.log("属性名:", attrname);
}
function URLInfoDecorator(targetClassPrototype) {
console.log("==========类装饰器============");
console.log("targetClassPrototype:", targetClassPrototype);
}
function constructorDecorator(params) {
return function (targetClassPrototype, paramname, paramindex) {
console.log("==========构造器参数装饰器============");
console.log("构造器参数装饰器", targetClassPrototype);
console.log("构造器参数名为:", paramname);
console.log("构造器参数索引位置:", paramindex);
};
}
var URLInfo = /** @class */ (function () {
function URLInfo(uri) {
this.uri = uri;
this.url = "https://www.imooc.com";
}
URLInfo.prototype.methodOne = function (data) {
console.log("this:", this);
console.log("目标类:", this.uri);
};
URLInfo.prototype.methodTwo = function (address) {
console.log(address);
};
__decorate([
UrlPropDecorator
], URLInfo.prototype, "url", void 0);
__decorate([
firstMethodDecorator,
__param(0, paramDecorator)
], URLInfo.prototype, "methodOne", null);
__decorate([
secondMethodDecorator("yes"),
__param(0, paramDecorator)
], URLInfo.prototype, "methodTwo", null);
URLInfo = __decorate([
URLInfoDecorator,
__param(0, constructorDecorator("url"))
], URLInfo);
return URLInfo;
}());
9. reflect-metadata
1. 什么是元数据?
元数据指附加在对象、类、方法、属性丶参数上的数据。
2. 元数据作用
元数据用来帮助提供实现某种业务功能需要用到的数据。
Reflect.defineMetadata
Reflect.defineMetadata 是一个重载的方法
// 1.1 为类或者对象上定义元数据
Reflect.defineMetadata(metakey,metavalue,targetClassOrObject)
// 1.2 为方法定义元数据
Reflect.defineMetadata(metakey,metavalue,targetprototype,methodname)
// 1.3 为属性定义元数据
Reflect.defineMetadata(metakey,metavalue,targetprototype,propkey)
// 说明:打开 d.ts 定义描述文件说明:Reflect 是命名空间,defineMetadata 是命名空间中的一个方法。
// 1.4 在对象上定义元数据
Reflect.defineMetadata('firstdescribe', '对象属性全部符合要求', obj);
Reflect.defineMetadata('seconddescribe', '对象不可删除', obj);
// 获取obj上metakey为 firstdescribe 的值
console.log(Reflect.getMetadata('firstdescribe', obj))// 输出对象属性全部符合要求
// 获取obj上metakey不存在的值
console.log(Reflect.getMetadata('threedescribe', obj))// 输出undefined
// 1.5 使用 Reflect.defineMetadata 在对象属性上定义元数据。
// 在对象属性上定义和获取元数据
Reflect.defineMetadata('usernamemetakey', '用户名合法', obj,"username");
Reflect.getMetadata('usernamemetakey', obj, "username"));// 输出用户名合法
// 使用 Reflect.hasMetadata 查看对象或对象属性上是否存在某个元数据
if (Reflect.hasMetadata('describe', obj)) {
console.log("obj存在describe元数据");
}
在类,方法上定义元数据
// 1. 在类上定义元数据
@Reflect.metadata('decribe', '都是地球人')
class People {
@Reflect.metadata("descible", "姓名不能包含非法汉字")
username = "wangwu"
@Reflect.metadata("importinfo", "去吃陶然居好吗")
eat() {
}
}
// 2
// 2.1 获取类上的元数据
console.log(Reflect.getMetadata('decribe', People));// 都是地球人
// 2.2 获取方法上的元数据 第二个参数是原型
console.log(Reflect.getMetadata('importinfo', People.prototype, 'eat'));//去吃陶然居好吗
// 2.3 判断People.prototype 原型上 eat 方法上是否存在importinfo元数据
if (Reflect.hasMetadata('importinfo', People.prototype, 'eat')) {
console.log("hasMetadata=>People原型上存在eat方法的importinfo元数据");
}
// 3 定义子类
class ChinesePeople extends People {
guoYear() {
}
}
// 4 子类获取父类原型上的方法 ———— hasMetadata
if (Reflect.hasMetadata('importinfo', ChinesePeople.prototype, 'eat')) {
console.log("hasMetadata=>ChinesePeople原型上通过继承也获取到了eat方法和eat方法的importinfo元数据");
}
// 5 获取自有元数据,但不能获取原型链上父类的元数据 ———— hasOwnMetadata
if (Reflect.hasOwnMetadata('importinfo', ChinesePeople.prototype, 'eat')) {
console.log("hasOwnMetadata=>ChinesePeople原型上存在eat方法的importinfo元数据");
} else {
console.log("hasOwnMetadata=>ChinesePeople原型上不存在eat方法的importinfo元数据");
}
直接在类属性上定义元数据
// 为类定义元数据
@Reflect.metadata("info", "地球人")
class People {
@Reflect.metadata('descible1', '居住地为主要城市')
@Reflect.metadata('descible2', '上海')
place: Array<string> = ["中国", "北京"]
@Reflect.metadata('firstname', '第一个名字')
@Reflect.metadata('lastname', '最后一个名字')
getFullName(name: string, age: string): number {
return 100
}
}
// 获取元数据
console.log(Reflect.getMetadata('info', People));//地球人
console.log(Reflect.getMetadata("descible", People.prototype, 'place'));//rose
console.log(Reflect.getMetadata('firstname', People.prototype, 'getFullName'))//Jim
console.log(Reflect.getMetadata('lastname', People.prototype, 'getFullName'))//Jim
// [
// 'design:returntype',
// 'design:paramtypes',
// 'design:type',
// 'lastname',
// 'firstname'
// ]
// // 获取People.prototype 上getFullName方法的全部元数据Key组成的数组
//console.log(Reflect.getMetadataKeys(People.prototype, "getFullName"));
// Reflect.getMetadataKeys(People.prototype).forEach((item) => {
// console.log("metadatakey:", item);
// })
// Reflect.getMetadataKeys(People.prototype, 'getFullName').forEach((metakey) => {
// console.log("11metadatakey:", metakey);
// console.log(Reflect.getMetadata(metakey, People.prototype, 'getFullName'));
// })
// 获取People类上place方法的全部元数据Key组成的数组
// 输出
// [
// 'design:type',
// 'descible1',
// 'descible2'
// ]
console.log(Reflect.getMetadataKeys(People.prototype, "place"));
Reflect.getMetadataKeys(People.prototype, 'place').forEach((metakey) => {
console.log("属性metadatakey:", metakey);
console.log(Reflect.getMetadata(metakey, People.prototype, 'place'));
})
class ChinesePeople extends People {
@Reflect.metadata("descible", "姓名不能包含非法汉字")
guoYear(args: string) {
}
}
console.log("getMetadataKeys==>查看父类上的方法...");
console.log(Reflect.getMetadataKeys(ChinesePeople.prototype, 'getFullName'))//Jim
console.log("getOwnMetadataKeys不能查看父类上的方法...");
console.log(Reflect.getOwnMetadataKeys(ChinesePeople.prototype, 'getFullName'))//Jim
在装饰器内部为类,方法,属性定义或者获取元数据
// 直接为类或属性或方法定义元数据
// 为类定义元数据
@Reflect.metadata('decribe', '都是地球人')
class People {
username = "wangwu"
eat() {
}
}
@Reflect.metadata('decribe', '木星和太阳')
class Customer {
username = "wangwu"
eat() {
}
}
// 获取元数据
console.log(Reflect.getMetadata('decribe', People));// 都是地球人
3 个重要且特殊的内置元数据
- design:paramtypes
a. 构造器所有参数数据类型组成的数组
b. 类中方法全部参数的数据类型组成的数组- design:type
a. 获取类属性的数据类型
b. 获取类方法参数的数据类型- design:returntype
a. 获取类方法返回值的数据类型