🐱个人主页:不叫猫先生
🙋♂️作者简介:前端领域新星创作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!
💫系列专栏:vue3从入门到精通、TypeScript从入门到实践
📢资料领取:前端进阶资料以及文中源码可以找我免费领取
🔥社群招募:博主建立了一个前端交流群,汇集了各路大神,期待你的加入!(文末有我wx,或者私我)
目录
- 专栏介绍
- 装饰器
- 1、类装饰器
- (1)一个装饰器
- (2)装饰器累加
- (3)装饰器传参
- 2、方法装饰器
- 3、属性装饰器
- 4、参数装饰器
- 5、实际应用
- (1)错误信息自定义
专栏介绍
TypeScript从入门到实践专栏是博主在学习和工作过程中的总结,实用性非常强,内容会不断进行精进,欢迎订阅哦,学会TS不迷路。
TS系列 | 标题 |
---|---|
基础篇 | TS入门(一) |
基础篇 | TS类型声明(二) |
基础篇 | TS接口类型(三) |
基础篇 | TS交叉类型&联合类型(四) |
基础篇 | TS类型断言(五) |
基础篇 | TS类型守卫(六) |
进阶篇 | TS函数重载(七) |
进阶篇 | TS泛型(八) |
进阶篇 | TS装饰器(九) |
装饰器
**装饰器(Decorator)**是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,装饰器的本身。其本身是一个函数,会在运行的时候被调用,被装饰的声明信息会作为参数传递给装饰器函数,当作形参。装饰器本质上主要是在操作原型对象,通过给原型对象 prototype
添加一些方法和属性,来扩展类的功能。
装饰器主要分为类装饰器(接收1个参数)、属性装饰器(接收2个参数)、方法装饰器(接收3个参数、参数装饰器(接收3个参数),不同装饰器接收参数也不一同。另外TS内置了装饰器类型,我们直接用就好了。
装饰器分类 | ts内置装饰器类型 | 接收参数 |
---|---|---|
类装饰器 | ClassDecorator | 1个,类函数 |
方法装饰器 | MethodDecorator | 3个,类函数,方法名,成员属性描述符 |
属性装饰器 | PropertyDecorator | 2个,类函数、属性名称 |
参数装饰器 | ParameterDecorator | 3个,类函数,参数名,参数所在位置的索引 |
要想在 TypeScript
中使用装饰器,必须将 tsconfig.json
中 experimentalDecorators
设置为true
,具体如下所示:
- 生成
tsconfig.json
文件
tsc --init
- 配置
tsc --target ES5 --experimentalDecorators
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
}
}
- 语法
@expression
,expression
是一个函数,位置根据不同类型的装饰器要求放置。
1、类装饰器
(1)一个装饰器
类装饰器接收一个构造函数作为参数,参数的类型是一个函数。
function ClassDecorator(target: Function): void {
target.prototype.start = function(): void {
// 通用功能
console.log('1')
}
}
@ClassDecorator
class Course {
constructor() {
// 业务逻辑
}
}
let course = new Course();
console.log('course',course.start())
(2)装饰器累加
装饰器不是只能定义一个,可以定义多个作用于一个类函数,通过装饰器累加从而给类追加多个方法和属性,可以用来监视、修改、替换类定义。
- 参数:只有
target
一个,意思是类函数本身
function StartTime(target: Function): void {
target.prototype.start = function(): void {
// 通用功能
console.log('start')
}
}
function EtartTime(target: Function): void {
target.prototype.end = function(): void {
// 通用功能
console.log('end')
}
}
@StartTime
@EndTime
class Course {
constructor() {
// 业务逻辑
}
}
let course = new Course();
console.log('course',course.start())
console.log('course',course.end())
(3)装饰器传参
装饰器传参,那么怎么接受呢,参数如何放置,只需要在内部再返回一个装饰器就可,参数在外部进行接收,具体例子如下所示:
function ClassDecorator(name: string) {
return (target: any) => {
target.prototype.name = name
}
}
@ClassDecorator('zhangsan')
class Course {
constructor() {
// 业务逻辑
}
}
let course:p = new Course();
interface p {
name?:string
}
console.log('course',course.name)//zhangsan
2、方法装饰器
方法装饰器使用与类装饰器基本相同,方法装饰器需要放在类方法的前面,方法作为参数传给方法装饰器,接收三个参数,具体如下:
- target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- propertyKey: 方法名称。
- descriptor: 成员的属性描述符。
//PropertyDescriptor类型是ts内置的
function MethodsDecorator (target: Object, propertyKey: string, descriptor: PropertyDescriptor){
console.log(target)
console.log(propertyKey)
console.log(descriptor)
}
class Person {
name: string = ''
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
@MethodsDecorator
getName() {
return this.name
}
@MethodsDecorator
getAge() {
return this.age
}
}
const p = new Person('张三', 18)
p.getName() // getName:张三
p.getAge() // getAge:18
我们可以看到打印出来的值,getName
、getAge
被一一打印出来以及属性描述符。其中属性描述符主要包括以下四个属性:
- configurable:是否可删除
- enumerable:是否可枚举
- value:属性值
- writable:是否可修改
3、属性装饰器
属性装饰器只接收两个参数,具体如下:
- target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- propertyKey:属性名称
const propertyName = (target: Object, propertyKey: string) => {
console.log(target, propertyKey)
}
class Person {
@propertyName
name: string = ''
@propertyName
age: number = 0
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
const p = new Person('张三', 18)
打印结果如下所示:
注意:如果@propertyName
只写在name: string = ''
上面,则只会打印出name
相关的数据。
4、参数装饰器
参数装饰器用于装饰函数的参数,与方法装饰器一样接收三个参数,具体如下:
- target:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- propertyKey:属性名称。
- paramIndex::参数所在位置的索引
const paramDecorator = (target: any, propertyKey: string, paramIndex: number) => {
console.log(target, propertyKey, paramIndex)
}
class Person {
name: string = '';
age: number = 0;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
setName(age:number,@paramDecorator name: string) {
this.name = name;
this.age = age;
}
}
const p = new Person('张三', 18)
p.setName(1,'李四')
打印结果如下所示:
5、实际应用
(1)错误信息自定义
怎么使用装饰器进行错误信息自定义,这里使用方法装饰器来对传进来的函数进行处理,主要步骤如下:
- 解构参数
- 取到传进来的函数
- 使用
try...catch
执行函数,新的报错定义在catch
中
const ErrorDecorator:MethodDecorator = (...args: any[]) => {
const [,,descriptor] = args;
const method = descriptor.value
descriptor.value = () =>{
try {
method()
} catch (error) {
console.log('这是新定义的错误')
}
}
}
class User {
@ErrorDecorator
public errorMsg () {
throw new Error('报错了')
}
}
new User().errorMsg()
在使用装饰器时先将原有的值进行存储一下,再去使用,以此来确保它使用的是类中的方法中的值。