概念
装饰器可以附加给类的声明中属性,方法上等,动态的对他们进行扩展。
装饰器是一项实验性特性,在vscode中使用的话会提示:
对修饰器的实验支持功能在将来的版本中可能更改。在 “tsconfig” 或 “jsconfig” 中设置 “experimentalDecorators” 选项以删除此警告。
使用
普通装饰器(无传参)
// 装饰器是用一个函数去定义
function logClass(param: any){
// param这个参数能拿到对应加了装饰器的类是个function
param.age = 1; // 这样加是没有用的
param.prototype.fn = function(): void {} // 拓展了fn这个方法
}
@logClass // 装饰器要这样加到类上
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
let p = new Person('xiaoming')
p.fn() // 虽然能执行但是编译器提示没有这个新添的方法
装饰器工厂(可传参)
// 装饰器是用一个函数去定义
function logClass(param: string){
// 此时param这个参数拿到的是传进来的参数
return function(target: any){
// target拿到的才是加了装饰器的类
}
}
@logClass('param') // 装饰器要这样加到类上
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
let p = new Person('xiaoming')
对比着前面的普通装饰器(无传参)来看
类的重载(类装饰器)
const extension = (constructor: Function) => {
constructor.prototype.meeting = () => { // 这里写重载
console.log('重载');
}
}
@extension
class Employee {
public name!: string
constructor(name: string) {
this.name = name
}
meeting() {}
}
let e: any = new Employee('nick')
e.meeting() // 重载
属性装饰器
以上都是属于类装饰器的使用方法,接下来是针对类属性的装饰器:
function attrName(params:any) {
// params为传入的参数
return function(targetProto: any, attr: string){
// 此时targetProto为类的原型(静态成员来说是类的构造函数,对于实例成员是类的原型对象),attr为加了类中加了装饰器的属性。
targetProto[attr] = params
}
}
class Person {
@attrName('修改1')
name: string
@attrName('修改2')
speak: string | undefined
constructor(name: string) {
this.name = name
}
}
let p = new Person('xiaoming')
console.log(p.name) // 装饰器是类实例化的时候就生效,所以打印出xiaoming
console.log(p.speak) // 修改2
方法装饰器
针对类方法的装饰器:
重写方法
function setFn(params: any){
return function(target: any, methodName: any, desc: any) {
// console.log(params) // 传进来的参数
// console.log(target) // 静态成员来说是类的构造函数,对于实例成员是类的原型对象
// console.log(methodName) // 加修饰器的方法
// console.log(desc) // 方法的描述,通过desc.value可以获取对应方法
desc.value = function(num: number){ // 参数需要这样接收
console.log(num)
}
}
}
class Person {
name: string | undefined
constructor() {}
@setFn('wow') // 方法修饰器
fn(){
console.log(this.name)
}
}
let p = new Person()
p.fn(2) // 不过这里会有错误提示,所以方法重写的时候,最好和原来的参数写法一致
方法修改
function setFn(params: any){
return function(target: any, methodName: any, desc: any) {
const fn = desc.value
desc.value = function(num: number){ // 参数需要这样接收
console.log(num+99)
fn.call(this, num) // 这句话意思是改变this指向,相当于把原来方法的肉填到这
}
}
}
class Person {
name: string | undefined
constructor() {}
@setFn('wow') // 方法修饰器
fn(num: number){
console.log(num)
}
}
let p = new Person()
p.fn(2)
// [LOG]: 101
// [LOG]: 2
方法参数装饰器
很少用,我觉得了解一下即可
function log(param: string) {
console.log(param)
return function (target: any, name: string, index: number) {
console.log(index)
// target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
// name: 成员的名字
// descriptor: 成员的属性描述符
}
}
class Employee {
fn(@log('IT') department: string, @log('John') name: string) {
console.log('给参数加上修饰器')
}
}
装饰器顺序
如果同时声明了类、属性、方法、方法参数装饰器,那么执行的顺序是:
属性---->方法---->方法参数----->类
如果同一种装饰器同时声明多次,那么执行顺序是从下到上。