TypeScript
装饰器
装饰器是一种特殊类型的声明,他能被附加到类声明,方法属性或参数上,可以修改类的行为
本质上就是一个方法,可以注入到类、方法、属性参数上来拓展类、属性、方法、参数的功能
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
类装饰器
类装饰器在类声明之前被声明(紧靠着类声明)
类装饰器应用于类构造函数,用来监视,修改或替换类定义
参数1:为装饰器装饰的构造函数
function logClass(params:any){
console.log(params); // params为装饰的构造函数HttpClient
params.prototype.apiUrl='动态拓展的属性' // 为构造函数HttpClient的原型添加属性
}
@logClass
class HttpClient{
constructor(){
}
getData(){
}
}
类的工厂装饰器
需要装饰器传参时必须在装饰器函数中return一个函数 此时返回的函数才是装饰器函数
参数1:为装饰器函数的传参
function logClass(params:string){
return function(target:any){
console.log(target); // target为装饰的构造函数HttpClient
console.log(params); // params为装饰器的传参
}
}
@logClass('hello')
class HttpClient{
constructor(){
}
getData(){
}
}
类的重载
function logClass(target: any) {
console.log(target);
return class extends target {
apiUrl:any = '修改后的数据'
getData(){
console.log(this.apiUrl);
}
}
}
@logClass
class HttpClient {
public apiUrl:string | undefined
constructor() {
this.apiUrl = '构造函数中的apiUrl'
}
getData() {
console.log(this.apiUrl);
}
}
const http = new HttpClient();
http.getData();
通过装饰器中进行类继承构造函数可以实现类的重载
修改类中的属性方法并实现(原先类中的方法会被覆盖)
属性装饰器
属性装饰器表达式会在运行时当做构造函数被调用 传入两个参数
参数1:对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象
参数2: 成员的名字
function logProperty(params: any) {
return function(target:any, attr:any) {
console.log(target); // 构造函数
console.log(attr); //url --成员的命名
target[attr] = params;
}
}
class HttpClient {
@logProperty('xxx')
public url: any | undefined;
constructor() {
}
getData() {
console.log(this.url); // 被装饰器修改后的属性 为 xxx
}
}
const http = new HttpClient();
http.getData();
方法装饰器
应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义
方法装饰器在运行时传入三个参数
参数1:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
参数2:成员的名字
参数3:成员的属性描述符
function get(params: any) {
return function(target:any, methodName:any, desc:any) {
console.log(target); // 构造函数
console.log(methodName); // 装饰器的方法名
console.log(desc); // 属性描述符的对象 desc.value为装饰的函数
target.apiUrl = 'xxx'; // 为实例对象添加属性和方法
target.run = function(){
console.log('run');
}
}
}
class HttpClient {
public url: any | undefined;
constructor() {
}
@get('123')
getData() {
console.log(this.url);
}
}
const http = new HttpClient();
console.log(http.apiUrl);
http.run();
通过方法装饰器来拓展原方法的功能
function get(params: any) {
return function(target:any, methodName:any, desc:any) {
console.log(target);
console.log(methodName);
console.log(desc);
let oldMethod = desc.value // 将旧方法保存在oldMethod中
desc.value = function(...argus:any[]){ // 将getData重新赋值并补充业务逻辑
argus = argus.map((ele) => {
return String(ele);
})
oldMethod.apply(this,argus) // 调用新方法并将this绑定回实例对象
}
}
}
class HttpClient {
public url: any | undefined;
constructor() {
}
@get('123')
getData(...argus:any[]) {
console.log(argus); // 打印出装饰器处理过的argus
}
}
const http = new HttpClient();
http.getData(123, 'xxx')
方法参数装饰器
参数装饰器表达式会在运行时当做函数被调用,可以使用参数装饰器为类的原型增加一些元素数据
调用时传入三个参数
参数1:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
参数2:参数的名字
参数3:参数在函数参数列表中的索引
function logParams(params: any) {
return function(target:any, methodName:any, paramsIndex:any) {
console.log(params); // 装饰器的传参
console.log(target); // 装饰器的构造函数
console.log(methodName); // 装饰的方法名
console.log(paramsIndex); // 参数的索引 为0
}
}
class HttpClient {
public url: any | undefined;
constructor() {
}
getData(@logParams('xxx') uuid:any) {
console.log(uuid); // 打印出实参123
}
}
const http = new HttpClient();
http.getData(123)
装饰器的执行顺序
属性装饰器 > 方法装饰器 > 方法参数装饰器 > 类装饰器
如果有多个同样的装饰器,会先执行后面的
function logClass1(params:string){
return function(target:any){
}
}
function logClass2(params:string){
return function(target:any){
}
}
@logClass1('hello')
@logClass2('hello') // 先执行装饰器logClass2 再执行logClass1
class HttpClient{
constructor(){
}
getData(){
}
}