一文搞懂 TypeScript 装饰器
?本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
?装饰器.png前段时间为了开发 HarmonyOS 路由框架,学习了 TypeScript 装饰器。在学习中发现无论是官方文档还是线上资料,鲜有文章可以将装饰器简单通俗的表达清楚,故而萌生了撰写此文的想法。
当然,作为一个前端门外汉、TS初学者,写这篇文字更主要的还是为了加深自己对装饰器的理解。新手上路,难免有不足之处,还请各位看官轻喷。
一.什么是装饰器?官方文档上的解释是:
?装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或参数上。装饰器使用@表达式这种形式,表达式求值后必须为一个函数,会在运行时当被调用时,被装饰的声明信息做为参数传入。
?这段官方解释了几个信息:
装饰器用在类、属性、方法或者参数等之上;
?注:可修改、替换被装饰的目标,也可已在不影响原有特性的前提下增加附属功能。
?使用@+函数(或者说表达式)的形式来书写;
该函数在运行时被调用,函数入参通常为与被装饰对象相关的一些信息;
这个函数或者不返回值,或者返回一个可以替换被装饰的目标的对象。(这一点官方解释里未说明)
示例代码如下:
functionMyDecorator(target:any){
...
}
@MyDecorator
classUser{
...
}
熟悉Java的同学,现在应该发现了,这不就是java中的注解吗!不同的是TypeScript装饰器只在运行时被调用,而Java中的注解能作用于源码阶段、编译阶段和运行时。
二.装饰器的分类装饰器的分类从写法上来区分,可分为:
普通装饰器(不可带参数)装饰器工厂(可以带参数)从类别上来区分,TypeScript中装饰器主要分为以下五类:
类装饰器 ClassDecorator属性装饰器 PropertyDecorator方法装饰器 MethodDecorator参数装饰器 ParameterDecorator访问符装饰器 AccessorDecorator前四类装饰器比较常用,后面的章节我们将分别介绍它们的定义以及实现方法、应用场景。
三.装饰器的写法这里用两段代码来分别演示普通装饰器和装饰器工厂的写法,它们的主要区别是:「普通装饰器不可带参数,而装饰器工厂可以带参数。」
3.1 普通装饰器直接将函数作为装饰器函数,我们称之为普通装饰器。
/**
*类装饰器(普通装饰器写法)
*/
exportfunctionRoute(target:object){
//TODO
...
}
//=========================================================
/**
*使用Route装饰器:
*/
@Route()
structDetailPage{
build(){
...
}
}
3.2 装饰器工厂装饰器工厂函数写法,可以在使用时传参,例如下面的routePath就是入参。
/**
*类装饰器(装饰器工厂实现)
*/
exportfunctionRoute(routePath:string):ClassDecorator{
return(target:object)={
//TODO
...
}
}
//=========================================================
/**
*使用Route装饰器:
*/
@Route({routePath:'/jump/entry/detail'})
structDetailPage{
build(){
...
}
}
在装饰器工厂函数中,首先会执行Route(),然后在使用返回的匿名器作为装饰器的实际逻辑。
三.装饰器分类3.1 类装饰器(ClassDecorator)?类别装饰器在类别声明之前被声明(紧靠着类别声明);类别装饰器检查类别构造函数,可以用于监视、修改或替换类别定义。
类装饰器不能用在声明文件中( .d.ts ),也不能用在任何外部上下文中(比如声明的类)类装饰器表达式会在运行时调用函数,类的构造函数作为其唯一的参数。如果类装饰器返回一个值,它会使用提供的重构函数来替换类的声明。?简单来说,类装饰器是直接作用在类装饰器上的,类装饰器的类型描述如下:
typeClassDecorator=TFunctionextendsFunction(
target:TFunction
)=TFunction|void;
它的参数只有一个:
target:对于类装饰器来说,目标就是这个类本身,或者说是类的构造函数。示例代码:
functionCustomClassDecorator(info:string):ClassDecorator{
return(target:Function)={
console.log(target)//[Functionuser]
console.log(info)//你好
}
}
@CustomClassDecorator('你好')
classUser{
publicname!:string
constructor(){
this.name='马东锡'
}
}
3.2 属性装饰器(PropertyDecorator)?装饰器属性声明在一个属性声明之前(紧靠着属性声明)。
属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如声明的类)里。属性装饰器表达式会在运行时调用函数,设置以下2个参数:对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。成员的名字。?属性装饰器可以定义在类的属性上,它的类型描述如下:
typePropertyDecorator=(
target:Object,
propertyName:string|symbol
)=void;
它的参数有两个:
target:对于属性装饰器来说,装饰器被应用在类的实例属性上。在这种情况下,目标参数是一个空对象{},表示在装饰器内部访问的对象,即被装饰类的实例propertyName:属性名示例代码:
functionCustomPropertyDecorator(userName:string):PropertyDecorator{
return(target:Object,
propertyName:string|symbol)={
console.log(target);//{}
console.log(propertyName);//userName
targetClassPrototype[propertyName]=userName
}
}
classUser{
@CustomPropertyDecorator('马东锡')
publicuserName!:string
constructor(){
}
}
letuser=newUser()
console.log(user.userName)//马东锡
3.3 方法装饰器(MethodDecorator)?方法装饰器声明在一个方法的声明(紧靠着方法声明之前)。它会被应用到方法的属性声明上,可以用于监视,修改或者替换方法定义。
装饰器方法不能用在声明文件( .d.ts ),重载或者任何外部上下文(比如声明的类)中;装饰器的方法表达式会在运行时调用函数,并确定以下 3 个参数:对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。成员的名字。成员的属性接口如果方法装饰器返回一个值,它会被初始化方法的属性控件。?装饰器顾名思义就是定义在类中方法上的装饰器,他的类型描述如下:
typeMethodDecorator=T(
target:Object,
methodName:string|symbol,
propertyDescriptor:TypedPropertyDescriptor
)=TypedPropertyDescriptor|void;
它接收三个参数:
target:对于方法装饰器和属性装饰器来说,target参数是一个对象,表示类的原型,其属性是被装饰的方法。因此,target参数实际上是指向了被装饰方法的引用methodName: 方法名propertyDescriptor: 属性接口示例代码如下:
functionCustomMethodDecorator(info:string):MethodDecorator{
return(target:Object,
methodName:any,
propertyDescriptor:PropertyDescriptor)={
console.log(target)//{sayHello:[Function(anonymous)]}
console.log(methodName)//sayHello
letoriginMethod=propertyDescriptor.value
propertyDescriptor.value=function(...args:any){
console.log("before")
console.log("我"+info+"来了")//我马东锡来了
originMethod.call(this,args)
console.log("after")
}
}
}
classUser{
@CustomMethodDecorator('马东锡')
sayHello(){
console.log('执行sayHello()方法)')
}
}
3.4 参数装饰器ParammeterDecorator?参数装饰器声明在一个参数声明之前(紧靠着参数声明)。参数装饰器检查类构造函数或方法声明。
参数装饰器不能用在声明文件(.d.ts)中,重载或其他外部上下文(比如声明的类)里。参数装饰器表达式会在运行时调用函数,确定以下 3 个参数:对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。成员的名字。参数在函数参数列表中的索引。参数装饰器只能用于监视一个方法的参数是否被确定。参数装饰器的返回值会被忽略。?装饰器参数定义在方法参数上,其类型描述如下:
typeParameterDecorator=(
target:Object,
methodName:string|symbol,
parameterIndex:number
)=void;
参数装饰器接收三个参数:
target:意义同方法装饰器methodName:如果参数修饰器所在的方法为类的静态/实例方法时,则此参数为当前参数修饰器所在方法的方法名。如果参数修饰器所在的方法为类的构造函数参数修饰时,则该参数为undefined。parameterIndex:参数下标示例代码:
functionCustomParameterDecorator(tag:string):ParameterDecorator{
return(target:any,
methodName:string|symbol,
parameterIndex:number)={
console.log(tag);//装饰实例方法的参数
console.log(target);//{sayHello:[Function(anonymous)]}
console.log(methodName.toString());//sayHello
console.log(parameterIndex.toString());//0
}
}
classUser{
constructor(){
}
sayHello(@CustomParameterDecorator("装饰实例方法的参数")name:String){
console.log("你好,"+name);
}
}
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线