方法装饰器应用于类的方法,可以修改、替换或观察方法的行为。方法装饰器接收三个参数:目标对象、方法名和属性描述符。
方法装饰器是一个函数,接收三个参数。
function log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log(`装饰方法: ${propertyKey}`)
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b
}
}
方法装饰器的三个参数:
function inspect(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log("target:", target === Calculator.prototype)
console.log("propertyKey:", propertyKey)
console.log("descriptor:", descriptor)
}
class Calculator {
@inspect
add(a: number, b: number): number {
return a + b
}
}
方法装饰器可以修改方法的实现。
function log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value
descriptor.value = function (...args: any[]) {
console.log(`调用 ${propertyKey}`)
console.log(`参数: ${JSON.stringify(args)}`)
const result = original.apply(this, args)
console.log(`结果: ${result}`)
return result
}
return descriptor
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b
}
}
const calc = new Calculator()
calc.add(1, 2)
方法装饰器可以测量方法执行时间。
function measure(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value
descriptor.value = function (...args: any[]) {
const start = performance.now()
const result = original.apply(this, args)
const end = performance.now()
console.log(`${propertyKey} 执行时间: ${(end - start).toFixed(2)}ms`)
return result
}
return descriptor
}
class DataService {
@measure
async fetchData(): Promise<any> {
await new Promise((resolve) => setTimeout(resolve, 100))
return { data: "数据" }
}
}
async function main() {
const service = new DataService()
await service.fetchData()
}
main()
方法装饰器可以实现方法缓存。
function cache(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value
const cacheMap = new Map<string, any>()
descriptor.value = function (...args: any[]) {
const key = JSON.stringify(args)
if (cacheMap.has(key)) {
console.log("从缓存返回")
return cacheMap.get(key)
}
const result = original.apply(this, args)
cacheMap.set(key, result)
return result
}
return descriptor
}
class MathService {
@cache
fibonacci(n: number): number {
if (n <= 1) return n
return this.fibonacci(n - 1) + this.fibonacci(n - 2)
}
}
const service = new MathService()
console.log(service.fibonacci(10))
console.log(service.fibonacci(10))
方法装饰器可以验证参数。
function validate(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value
descriptor.value = function (...args: any[]) {
for (const arg of args) {
if (arg === undefined || arg === null) {
throw new Error(`${propertyKey} 参数不能为空`)
}
}
return original.apply(this, args)
}
return descriptor
}
class UserService {
@validate
createUser(name: string, email: string) {
return { name, email }
}
}
const service = new UserService()
service.createUser("张三", "test@example.com")
service.createUser("", "test@example.com")
方法装饰器可以将方法设为只读。
function readonly(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.writable = false
return descriptor
}
class Calculator {
@readonly
add(a: number, b: number): number {
return a + b
}
}
const calc = new Calculator()
calc.add = () => 0
方法装饰器可以接收参数。
function retry(attempts: number) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value
descriptor.value = async function (...args: any[]) {
let lastError: any
for (let i = 0; i < attempts; i++) {
try {
return await original.apply(this, args)
} catch (error) {
lastError = error
console.log(`第 ${i + 1} 次尝试失败`)
}
}
throw lastError
}
return descriptor
}
}
class ApiService {
@retry(3)
async fetchData(): Promise<any> {
if (Math.random() > 0.3) {
throw new Error("请求失败")
}
return { data: "成功" }
}
}
async function main() {
const api = new ApiService()
try {
const result = await api.fetchData()
console.log(result)
} catch (error) {
console.log("所有尝试都失败")
}
}
main()
方法装饰器应用于类的方法,接收目标对象、方法名和属性描述符三个参数。方法装饰器可以修改方法行为、添加日志、实现缓存、验证参数等。装饰器工厂可以让方法装饰器接收参数。方法装饰器是 AOP(面向切面编程)的重要实现方式。