一、ts 装饰器概念
装饰器:装饰器是一种特殊类型的声明,它能够被添加到类声明,方法,属性或参数上,可以修改类的形为。
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展 类、属性、 方法、参数 的功能。
常见的装饰器有: 类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
装饰器是过去几年的 js 最大的成就之一, 已是 ES7 的标准特性之一
所有装饰器后面都不可加分号 ;
二、类装饰器
类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视、修改、替换 类的定义。可传入一个参数
1. 类装饰器:普通装饰器(无法传参)
function logClass(params:any) {
// 这里的 parmas 默认表示被监视的类
// 给类 添加属性
params.prototype.name = 'xxl'
// 给类 添加方法
params.prototype.getUrl = function() {
return this.name + '123'
}
}
// 装饰饰后面不能加 分号 ;
@logClass
class Animal{
constructor() {
}
}
// 这里要加个:any,因为我们没有传入 类初始不用传参,是利用装饰器使得类加了属性
let dog:any = new Animal()
console.log(dog.name) // xxl
console.log(dog.getUrl()) // xxl123
2. 类装饰器:装饰器工厂(可传参)
function logClass(params:string) {
// 这里的 params 就是装饰器传过来的参数
// 这里的返回的函数就是装饰器监视的类
return function(target:any) {
// 给类 添加属性
target.prototype.url = params;
// 给类 添加方法
target.prototype.getUrl = function() {
return this.url + '123'
}
}
}
@logClass('www.baidu.com')
class Animal{
constructor() {
}
}
let d:any = new Animal()
console.log(d.url) // www.baidu.com
console.log(d.getUrl()) // www.baidu.com123
3. 类装饰器:重载构造函数
类装饰器:重载构造函数, 通过使用
return class extends target{ }
来重载
以下是一个重载构造函数的例子:
类装饰器表达式会在运行时当作函数被调用, 类的构造函数会作为其唯一的参数。 如果类装饰器返回一个值, 它会使用提供的构造函数【class extends target{ } 】来替换类的声明
function logClass(target:any) {
return class extends target{
apiUrl = '新的地址'
getUrl() {
return this.apiUrl + 'new'
}
}
}
@logClass
class Animal {
apiUrl:string | undefined;
constructor() {
this.apiUrl = '旧的地址'
}
getUrl() {
return this.apiUrl + 'old'
}
}
let d = new Animal()
console.log(d.getUrl())
三、属性装饰器
属性装饰器表达式会在运行时当作函数被调用,传入下列 2 个参数:
1、对于静态成员来说是类的构造函数,对于实例成员来说是 类的原型对象 2、属性成员的名字, 例如:name, age 等等。
function logProperty(params:any) {
// target 是类的原型对象
return function(target:any, attr:any) {
target[attr] = params;
console.log( target[attr], `属性为:${attr}`)
}
}
class Animal{
@logProperty(20)
total:number | undefined;
@logProperty('wwww.baidu.com')
apiUrl:string | undefined;
@logProperty('gg')
name:string | undefined;
constructor() {
}
getUrl() {
console.log('apiUrl的值为' + this.apiUrl)
}
}
let d = new Animal()
console.log(d.total, 'd.total') // 20 "属性为:total"
console.log(d.apiUrl, 'd.apiUrl') // wwww.baidu.com 属性为:apiUrl
console.log(d.name, 'd.name') // gg 属性为:name
d.getUrl() // apiUrl的值为wwww.baidu.com
四、方法装饰器
方法装饰器: 它会被应用到方法的 属性描述符上, 可以用来监视、修改或者替换方法定义, 同时可以衍生原来的方法的作用
方法装饰器在运行时传入下列 3 个参数
1. target: 对于静态成员来说是类的构造函数,对于实现成员来说是类的原型对象。
2. methodName: 方法成员的名字, 如 getUrl
3. desc: 方法成员的属性描述符
1. 方法装饰器一:修改类里的属性和方法
function get(params:any) {
return function(target:any, methodName:any, desc:any) {
console.log(target, 'target') //
console.log(methodName, 'methodName')
console.log(desc, 'desc')
// 建议先在类中定义一下,再进行修改,不然会报错 Property 'name' does not exist on type 'HttpClient'.
target.name = 'gg';
target.run = function() {
return '在跑步'
}
}
}
class HttpClient{
name:string | undefined;
apiUrl:string | undefined;
constructor() {}
@get('www.baidu.com')
getUrl() {
return this.apiUrl;
}
run() {
return '*****'
}
}
let http = new HttpClient()
console.log(http.name) // gg
console.log(http.run()) // 在跑步
2. 方法装饰器二:装饰方法
装饰方法就是把之前的方法先保留目前的操作,再在这基础上增加一些操作。这个功能我们可以通过用 对象冒充
apply()
或call()
来实现
function logMethods(params:any) {
return function(target:any, methodName:any, desc:any) {
// 先保存之前方法
let oMethod = desc.value;
// 再修改方法,例如:我们目前想把所有的参数转化为 string 类型
desc.value = function(...args:any[]) {
args = args.map((value) => {
return String(value)
})
// 再利用对象冒充的形式定义到我们之前的方法
oMethod.apply(this, args)
console.log(args, 2) // ["12", "abc"]
}
}
}
class HttpClient {
@logMethods('2222')
getUrl(...args:any[]) {
console.log(args, '1') // ["12", "abc"]
console.log('这是当前的方法操作相关')
}
}
let http = new HttpClient()
console.log(http.getUrl(12, 'abc'), '----')
五、方法参数装饰器
方法参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列 3 个参数
1. 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。
2. 方法的名字, 例如:getUrl
3. 参数在函数参数列表中的索引
function logParams(parmas:any) {
return function (target:any, methodName:any, paramsIndex:any) {
console.log('------')
console.log(parmas, 'parmas') // 这是装饰饰传的参数 parmas
console.log(target, 'target')
console.log(methodName, 'methodName') // getUrl methodName
console.log(paramsIndex, 'paramsIndex') // 0 "paramsIndex"
if(paramsIndex === 1) {
}
}
}
class HttpClinet{
getUrl(@logParams('这是装饰饰传的参数1') a:any,@logParams('这是装饰饰传的参数2') b:any) {
console.log(a, b)
}
}
let http = new HttpClinet();
http.getUrl('这是一个阳光', '好天气')
六、装饰器执行顺序
执行顺序 属性>方法>方法参数>类
注:如果有多个同样的装饰器,它会先执行后面的装饰器。
function logClassA(params:any) {
return function(target:any) {
console.log('类 装饰器----111')
}
}
function logClassB(params:any) {
return function(target:any) {
console.log('类 装饰器----222')
}
}
function logAttrA(params:any) {
return function(target:any, attr:any) {
console.log('属性 装饰器---' + `${params}`)
}
}
function logAttrB(params:any) {
return function(target:any, attr:any) {
console.log('属性 装饰器---' + `${params}`)
}
}
function logMethodsA(params:any) {
return function(target:any, methodName:any,desc:any ) {
console.log('方法 装饰器----' + `${params}`)
}
}
function logMethodsB(params:any) {
return function(target:any, methodName:any,desc:any ) {
console.log('方法 装饰器----' + `${params}`)
}
}
function logMethodsParamsA(params:any) {
return function(target:any, methodName:any,paramsIndex:any ) {
console.log('方法参数 装饰器----' + `${params}`)
}
}
function logMethodsParamsB(params:any) {
return function(target:any, methodName:any,paramsIndex:any ) {
console.log('方法参数 装饰器----' + `${params}`)
}
}
@logClassA('1111')
@logClassB('2222')
class HttpClient{
@logAttrA('1111')
@logAttrB('2222')
urlA:string | undefined
@logMethodsA('111')
@logMethodsB('222')
getUrlA(@logMethodsParamsA('111') name:any, @logMethodsParamsB('222') age:any) {
}
}
执行结果如下图: