---
title: "裝飾器"
date: "2026-04"
category: "TS"
tags: ["TS"]
description: "ts這個裝飾器從本質上來說仍然是一個高階函式，和py的裝飾器在使用場景上是差不多的：當我需要給某個物件進行一個「包裝」，但又不想整個重寫這個原有的方法時，那我就要用裝飾器..."
source: "https://enriquemark.com/zh-hant/posts/%E8%A3%85%E9%A5%B0%E5%99%A8"
---

ts這個裝飾器從本質上來說仍然是一個高階函式，和py的裝飾器在使用場景上是差不多的：當我需要給某個物件進行一個「包裝」，但又不想整個重寫這個原有的方法時，那我就要用裝飾器。比如給已有的方法加log，這在py裡面也是典型的@場景。

```typescript
function logger(target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value  // 拿到原方法
    
    descriptor.value = function(...args: any[]) {
        console.log(`调用 ${key}`)
        return original.apply(this, args)
    }
    // 不需要 return，直接改 descriptor.value
}

class Greeter {
    @logger
    greet(name: string) {
        console.log(`hello ${name}`)
    }
}
```

這裡的 `target, key, descriptor` 是ts的特色，或者說js的特色，本質上就是 `prototype` 機制需要的內容。`target` 直接指向這個方法或者類的原型鏈；而 `key` 比較簡單，就是這個方法大的名字；`descriptor` 是這個方法的「設定」，而我們一般只關心 `descriptor.value`，也就是這個方法本身：在這裡等價於py的func。

這也是ts麻煩的點。在py我可以直接return新的函式，裡面的內容就是單純func()，然後在它前面加我需要的邏輯包裝就行了。但是在ts這裡，我就得用一個很麻煩的機制重寫這個方法。

```ts
function logger(target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value  // 保存原方法

    descriptor.value = function(...args: any[]) {
        console.log(`调用 ${key}，参数:`, args)
        const result = original.apply(this, args)  // 调用原方法
        console.log(`${key} 返回:`, result)
        return result
    }

    // 不需要 return，直接改了 descriptor 就生效
}
```

從上面的整個邏輯就能看出來，想理解ts的@，關鍵就是理解js的原型鏈機制，一切方法的都是掛在原型鏈上的，你需要保證這個方法的正常性，就得把它的整個「全套」原封不動的帶回去。比如 `this`和`function`，不用箭頭函式的原因就是這裡會丟this，如果丟了，那我原先方法裡面的this操作就全沒——其實這裡就算不寫裝飾器也是一樣的。

this本身就是指向自己的上級例項，也就是py裡的self，我在類裡面呼叫屬性就要 `self.xxx` ，js這裡就是 `this.xxx`，所有方法都是掛在自己的上級原型鏈之下的，而上級原型鏈的屬性也是一樣。`this.xxx` 指的就是上級例項下的xxx，js預設會順著原型鏈查詢。而箭頭函式會丟失這個this上下文，它的this會指向定義時的外層物件，而不是當前呼叫物件。比如在這種包裝場景下，我要是用箭頭函式，指向的就是 `logger`，如果包裝物件是某個類下的方法，那他就會完全吃不到自己所屬類的屬性，因為這裡的this根本就沒指向它。所以這裡才得選擇 `function`，this直接指向呼叫方（被包裝物件）的上下文，然後在裡面傳的this才是指向正確的呼叫物件。

`original.apply(this, args)` 這一步在做的就是this上下文傳遞，保證this在最終結果上和包裝物件的一致，而不會因為我對它的重寫行為而丟失。
