【typescript进阶篇】(第一章) 装饰器

装饰器

介绍:装饰器是一种特殊类型的声明,它能够被附加到类,方法, 访问器,属性或参数上。用 @添加

​ 装饰器本质上还是一个函数,在别的语言中已广泛使用,如: python, 但在TS中依旧为一个测试中的版本,若要启 用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选

​ 若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项

添加到类上, 类装饰器

添加到方法上,方法装饰器

添加到访问器上,访问器装饰器

添加到属性上,属性装饰器

添加到参数上,参数装饰器

装饰器工厂 :如果我们要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。 装饰器工厂就是一个简 单的函数,它返回一个表达式,以供装饰器在运行时调用

类的装饰器

  • 类装饰器就在类声明之前被声明
  • 类装饰器被应用于类的构造函数,可以用来观察、修改或替换类定义
  • 类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的类)
  • 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数
  • 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
typescript 复制代码
// 1.类装饰器
//  类装饰器就在类声明之前被声明
//  类装饰器被应用于类的构造函数,可以用来观察、修改或替换类定义
//  类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中(比如declare的类)
//  类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数
//  如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。

/* function testDecorator(constructor: any) {
  constructor.prototype.uname = "张予曦"
  constructor.prototype.show = ():void => {
    console.log(`我是${constructor.prototype.uname}`);
    
  }
}

@testDecorator
class Person {

}


let p = new Person();
(p as any).show();
 */


// 使用工厂函数
function testDecorator(flag: boolean) {
  if(flag) {
    return function (constructor: any) {
      constructor.prototype.uname = "张予曦";
      constructor.prototype.show = (): void => {
        console.log(`我是${constructor.prototype.uname}`);
      };
    };
  }else {
    return function(constructor: any) {}
  }
  
}

@testDecorator(true)
class Person {}

let p = new Person();
(p as any).show();
typescript 复制代码
// 函数可以接收很多的参数,参数类型都是any,将他们放在一个数组中
// T 就相当于一个类,里面有构造函数
/* function testDecorator<T extends new(...args: any[]) => {}>(constructor: T) {
  // 直接对 constructor 做扩展
  return class extends constructor {
    name = "章若楠";
    show() {
      console.log(this.name);
      
    }
  }
}

@testDecorator
class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}


let p = new Person("陈意涵");
console.log(p);
(p as any).show()
 */

function testDecorator() {
  return function <T extends new (...args: any[]) => {}>(constructor: T) {
    return class extends constructor {
      name = "章若楠";
      show() {
        console.log(this.name);
      }
    };
  };
}

const Person = testDecorator()(class {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
})


// class Person {
//   name: string;
//   constructor(name: string) {
//     this.name = name;
//   }
// }

let p = new Person("陈意涵");
p.show();

方法装饰器

  • 方法装饰器写在在一个方法的声明之前
  • 方法装饰器可以用来监视,修改或者替换方法定义。
  • 方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
    • 静态成员的类的构造函数,或者实例成员的类的原型
    • 成员的名称
    • 该成员的属性描述符
typescript 复制代码
// 普通方法: target对应的就是 prototype
// 静态方法: target对应的就是 类的构造函数
function getNameDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
  console.log(target);
  console.log(key);
  console.log(descriptor);
  
  // descriptor.writable = false;
  descriptor.value = function() {
    return "decorator"
  }
} 


class Test {
  name: string = "郑合惠子";
  constructor(name: string){
    this.name = name;
  }
  @getNameDecorator
  getName() {
    return this.name;
  }

  static show():void {
    console.log("Hello MethodDecorator");
    
  }
}


let t = new Test("aaa")
// t.getName = () => {
//   return "Hello 张雪迎"
// }
console.log(t.getName());

访问器的装饰器

访问器装饰器 声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的 属性描述符 并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如 declare的类)里。

  • 方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
    • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    • 成员的名字。
    • 成员的名字。
  • TypeScript不允许同时装饰一个成员的get和set访问器。
typescript 复制代码
function visitDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
  descriptor.writable = false
}

class Test {
  private _name: string;
  constructor(name: string) {
    this._name = name;
  }
  get name() {
    return this._name;
  }
  @visitDecorator
  set name(newName: string) {
    this._name = newName;
  }
}

const t = new Test("周雨彤");
t.name = "钟楚曦"
console.log(t.name);

属性的装饰器

  • 属性装饰器写在一个属性声明之前(紧靠着属性声明)

  • 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:

    • 对于静态属性来说就是当前的类, 对于实例属性来说就是当前实例
    • 成员的名字
typescript 复制代码
/* function nameDecorator(target: any, key: string): any {

}

class Test {
  @nameDecorator
  name = "任敏"
}

let t = new Test();
t.name = "周洁琼"
console.log(t.name);
 */

function nameDecorator(target: any, key: string): any {
  // const descriptor: PropertyDescriptor = {
  //   writable: false
  // };
  // return descriptor;

  // 修改的并不是实例上的 name, 而是原型上的 name
  target[key] = '秦岚';
}
class Test {
  @nameDecorator
  // name 是放在实例上的
  name = "任敏"
}

let t = new Test();
t.name = "周洁琼"
console.log(t.name);
console.log((t as any).__proto__.name);

参数装饰器

  • 参数装饰器声明在一个参数声明之前(紧靠着参数声明)
  • 参数装饰器应用于类构造函数或方法声明
  • 数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文(比如 declare的类)里
  • 参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
    • 对于静态成员来说是当前的类,对于实例成员是当前实例。
    • 参数所在的方法名称。
    • 参数在参数列表中的索引。
typescript 复制代码
function paramDecorator(target: any, method: string, index: number) {
  console.log(target, method, index);
}

class Test {
  getInfo(name: string, @paramDecorator age: number) {
    console.log(name, age);
  }
}

const t = new Test();
t.getInfo('安悦溪', 18);

小案例

  • 需求: 利用装饰器来避免多次书写 try{} catch(e) {}
typescript 复制代码
/* const userInfo: any = undefined;

class Test {
  getName() {
    try {
      return userInfo.name;
    } catch (e) {
      console.log(e);
    }
  }
  getAge() {
    try {
      return userInfo.age;
    } catch (e) {
      console.log(e);
    }
  }
}

const t = new Test();
t.getName();
t.getAge(); */

/* const userInfo: any = undefined;

function catchError(target: any, key: string, descriptor: PropertyDescriptor) {
  const fn = descriptor.value;
  descriptor.value = function () {
    try {
      fn();
    } catch (e) {
      console.log("userInfo上该属性不存在");
    }
  };
}

class Test {
  @catchError
  getName() {
    return userInfo.name;
  }
  @catchError
  getAge() {
    return userInfo.age;
  }
}

const t = new Test();
t.getName();
t.getAge(); */


const userInfo: any = undefined;
function catchError(msg: string) {
  return function(target: any, key: string, descriptor: PropertyDescriptor) {
    const fn = descriptor.value;
    descriptor.value = function() {
      try {
        fn();
      } catch (e) {
        console.log(msg);
      }
    };
  };
}

class Test {
  @catchError('userInfo.name 不存在')
  getName() {
    return userInfo.name;
  }
  @catchError('userInfo.age 不存在')
  getAge() {
    return userInfo.age;
  }
}

const t = new Test();
t.getName();
t.getAge();
相关推荐
崔庆才丨静觅30 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax