类装饰器
基本语法
类装饰器时一个应用在类声明上的函数, 可以为类添加额外的功能, 或添加额外的逻辑
ts
/**
* Demo 函数会在 Person 类定义时执行
* 参数说明: target 参数是被修饰的类, 即 Person
*/
function Demo(target: Function) {
console.log(target)
}
@Demo
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
应用示例
需求: 定义一个装饰器, 实现
Person
实例调用toString
时返回JSON.stringify
的执行结果
ts
function CustomString(target: Function) {
target.prototype.toString = function() {
return JSON.stringify(this)
}
// 封锁
Object.seal(target)
}
@CustomString
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
const man = new Person('张三', 18)
// console.log(man.toString()) // [object Object]
// console.log(JSON.stringify(man)) // "{"name": "张三", "age": 18}"
console.log(man.toString()) // "{"name": "张三", "age": 18}"
interface Person {
x: number
}
// 封锁后不能向原型上添加属性
Person.prototype.x = 99
关于返回值
- 类装饰器有返回值: 若类装饰器返回一个新的类, 那这个新类将替换掉被装饰的类
- 类装饰器无返回值: 若类装饰器无返回值或者返回
undefined
, 那被装饰的类不会被替换
ts
function Demo(target: Function) {
// 装饰器有返回值时, 该返回值会替换掉被装饰的类
return class {
test() {
console.log(200)
console.log(300)
}
}
}
@Demo
class Person {
test() {
console.log(100)
}
}
console.log(Person)
关于构造类型
在TS
中, Function
类型所表示的范围非常广泛, 包括: 普通函数, 箭头函数, 方法等等
但并非所有Function
类型的函数都可以被new
关键字实例化, 如箭头函数是不能被实例化的
那么TS
中如何声明一个构造类型呢?
- 仅声明构造类型
ts
/**
* new 表示: 该类型是可以使用 new 关键字实例化
* ...args 表示: 构造器可以接受[任意数量]的参数
* any[] 表示: 构造器可以接受[任意类型]的参数
* {} 表示: 返回类型是对象, 非 null, 非 undefined 的对象
*/
// 定义 Constructor 类型, 其含义是构造类型
type Constructor = new (...args: any[]) => {}
function test(fn: Constructor) {}
class Person{}
test(Person)
test(() => {}) // 警告: 类型“() => void”提供的内容与签名“new (...args: any[]): {}”不匹配
- 声明构造类型并指定静态属性
ts
// 定义一个构造器,并且包含一个静态属性 wife
type ConstructorStatic = {
new (...args: any[]): {} // 构造签名
wife: string // wife 静态属性
}
function demo(fn: ConstructorStatic) {}
class Person {
static wife = 'Person'
}
demo(Person)
替换被装饰的类
对于高级一些的装饰器, 不仅仅是覆盖一个原型上的方法, 还要有更多功能, 例如添加新的方法和状态
需求: 设计一个
LogTime
装饰器, 可以给实例添加一个属性, 用于记录实例对象的创建时间, 再添加一个方法用于读取创建时间
ts
// 定义 Constructor 类型, 其含义是构造类型
type Constructor = new (...args: any[]) => {}
interface User {
getTime(): string
}
function LogTime<T extends Constructor>(target: T) {
return class extends target {
createTime: Date;
constructor(...args: any[]) {
super(...args)
this.createTime = new Date()
}
getTime() {
return `该对象创建时间为: ${this.createTime}`
}
}
}
@LogTime
class User {
constructor(
public name: string,
public age: number
) {}
speak() {
console.log(`我是${this.name}, 今年${this.age}岁`)
}
}
const man = new User('张三', 17)
console.log(man.getTime())