ts中的装饰器
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或参数上。装饰器使用@expression这种形式,expression必须是一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。
ts中使用装饰器,必须在tsconfig.json中配置才可以识别使用:启用如下命令: "experimentalDecorators": true
我们根据应用到不同的类型上定义不同的装饰器
- 添加到类上,类装饰器
- 添加到方法上,方法装饰器
- 添加到访问器上,访问器装饰器
- 添加到属性上,属性装饰器
- 添加到参数上,参数装饰器
装饰器工厂:如果我们要定制一个装饰器如何应用到一个声明上,我们的得写一个装饰器工厂函数,装饰器工厂就是一个简单的函数,他返回一个表达式,以供装饰器在运行时调用
1.类装饰器
- 类装饰器就在类声明之前被声明
- 类装饰器被应用于类的构造函数,可以用来观察,修改或者替换类定义
- 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数
- 如果类装饰器返回一个值,他会使用提供的构造函数来替换类的声明
我们先来是一个简单的类装饰器
//定义装饰器-就是一个函数
function testDecorator(constructor:any){ //类装饰器是用构造函数来作为唯一的参数,我们就传入constructor比较好理解
// 定义一个属性
constructor.prototype.name='taotao'
// 定义一个方法
constructor.prototype.show=():void=>{
console.log(`my name is ${constructor.prototype.name}`)
}
}
@testDecorator //可以扩展类的属性和方法
class Person{
}
let p =new Person()
p.show() //my name is taotao
console.log(p.name) //taotao
那么接下来我们提个小的需求,就是我们给构造器可以传入一个布尔值参数,来确定是否可以打印出内容 ,传入true 就可以打印;传入false,不可以打印。这个时候就需要装饰器工厂来帮忙:代码如下
function testDecorator(flag:boolean){
if(flag){
return function (constructor:any){ //类装饰器是用构造函数来作为唯一的参数,我们就传入constructor比较好理解
// 定义一个属性
constructor.prototype.name='taotao'
// 定义一个方法
constructor.prototype.show=():void=>{
console.log(`my name is ${constructor.prototype.name}`)
}
}
}else{
return function(constructor:any){} //不打印任务东西
}
}
@testDecorator(false) //可以扩展类的属性和方法
class Person{
}
let p =new Person()
p.show()
console.log(p.name)
当我们传入的参数是false时,控制台会报如下错误:
当我们传入的参数是true时,能够正确的输出
2.方法装饰器
- 方法装饰器写在一个方法的声明之前
- 方法装饰器可以用来监视,修改或者替换方法定义
- 方法装饰器表达式会在运行时当作函数调用,需要传入下列3个参数:
- 静态成员的类的构造函数,或者实例成员类的原型
- 成员的名称
- 该成员的属性描述符
定义一个方法的装饰器代码如下:
// 定义一个方法装饰器
// target 普通方法对应的是prototype 静态方法对应的是 类的构造函数
function getNameDecorator(target:any,key:string,desciptor:PropertyDescriptor){
// 我们打印一下看下这三个参数的具体的表示的内容
console.log(target)
console.log(key) //方法的名字getName
console.log(desciptor) // { 属性描述符
// value: [Function: getName],
// writable: true,
// enumerable: false,
// configurable: true
// }
// desciptor.writable=false //这个就是值方法getName不能被修改,如果修改就会报错
desciptor.value=function(){ //可以自定义方法的返回值和具体的逻辑代码
return 'new hello'
}
}
class Test{
name:string='taotao'
constructor(name:string){
this.name=name
};
@getNameDecorator
getName(){
return this.name
};
show():void{
console.log('hello show')
}
}
let t=new Test('hahahah')
// console.log(t)
// t.getName=function(){
// return 'heelo '
// }
console.log(t.getName()) //new hello
3.访问器装饰器
- 访问器装饰器声明在一个访问器声明之前(紧靠着访问器声明)。访问器装饰器应用于访问器的属性描述法 并且可以用来监视,修改或者替换一个访问器的定义。访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如declare的类)里
- 访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
- 对于静态成员来说是类的构造函数,对于实例来说是类的原型对象
- 成员的名字
- 描述对象 需要注意的一点是:typescript不允许同时装饰一个成员你的get和set访问器 一个访问器装饰器代码的例子:
// 定义访问器装饰器
function getNameDecorator1(target:any,key:string,desciptor:PropertyDescriptor){
console.log(target)
console.log(key)
console.log(desciptor)
// desciptor.writable=false
// desciptor.value=function(){
// return 'new taotao'
// }
}
class Test1{
private _name:string
constructor(name:string){
this._name=name
}
// 对于私有属性的访问器
@getNameDecorator1
get name(){
console.log('get')
return this._name
}
set name(newName){
console.log('set')
this._name=newName
}
}
let t1=new Test1('taotao')
t1.name='hlelo '
console.log(t1.name)
4.属性装饰器
- 属性装饰器写在一个属性声明之前(紧靠着属性声明)
- 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
- 对于静态属性来说就是当前的类,对于实例属性来说就是当前实例
- 成员的名字 属性装饰器的代码的例子:
function getName(target:any,key:string):any{
// 自己第三个属性让属性不能被修改
// const descriptor:PropertyDescriptor={
// writable:false
// }
// return descriptor //加上之后就不能修改属性值
target[key]='yuxuan' //原型链上的name属性
}
class Test{
@getName
name='taotao'
}
let t=new Test()
// t.name='coco' //此时能被修改
console.log(t.name)
console.log((t as any).__proto__.name) //yuxuan
5.参数装饰器
- 参数装饰器声明在一个参数声明之前(紧靠着参数声明)
- 参数装饰器应用于类构造函数或方法声明
- 参数装饰器不能用在声明文件中(.d.ts),重载或者任何外部上下文(比如declare的类)里
- 参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数
- 对于静态成员来说是类的构造函数,对于实例来说是类的原型对象
- 参数所在的方法的名字
- 参数在参数列表的索引
function getName(target:any,key:string,index:number){
console.log(target)
console.log(key)
console.log(index)
}
class Test{
getInfo(@getName name:string,age:number){
console.log(name,age)
}
}
let t=new Test()
t.getInfo('taotao',28)
6.装饰器的小例子:try....catch...利用装饰器封装
const userInfo:any=undefined
class Test{
getName(){
return userInfo.name
}
getAge(){
return userInfo.age
}
}
let t=new Test()
t.getName() //会报userInfo不存在name属性 我们使用try...catch来增加代码的兼容性
t.getAge()
加上try....catch...
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)
}
}
}
let t=new Test()
t.getName()
t.getAge()
虽然加上了try...catch...,但是我们如果需要加的地方比较多呢,我们用方法装饰器来实现
const userInfo:any=undefined
//定义方法装饰器
const getErrorDecorator=function (target:any,key:string,descriptor:PropertyDescriptor){
const fn=descriptor.value
descriptor.value=function(){
try{
fn()
}catch(e){
console.log('userinfo上不存在该属性')
}
}
}
class Test{
@getErrorDecorator //使用方法装饰器
getName(){
return userInfo.name
}
@getErrorDecorator //使用方法装饰器
getAge(){
return userInfo.age
}
}
let t=new Test()
t.getName() //userinfo上不存在该属性
t.getAge() //userinfo上不存在该属性
但是我们如果想打印不同的错误信息,该如何操作呢?需要装饰器工厂来帮忙
const userInfo:any=undefined
//定义装饰器工厂,返回一个装饰器
function getErrorDecorator(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{
@getErrorDecorator('userinfo上不存在name属性')
getName(){
return userInfo.name
}
@getErrorDecorator('userinfo上不存在age属性')
getAge(){
return userInfo.age
}
}
let t=new Test()
t.getName() //userinfo上不存在name属性
t.getAge() //userinfo上不存在age属性