[TOC]
隨著 ES6 和 TypeScript 中類的引入,在某些場景需要在不改變原有類和類屬性的基礎上擴展些功能,這也是裝飾器出現的原因。
裝飾器是一個還處于草案中的特性,目前木有直接支持該語法的環境,但是可以通過 babel 之類的進行轉換為舊語法來實現效果,所以在TypeScript中,可以放心的使用`@Decorator`。
# 裝飾器
修飾器對類的行為的改變是在代碼編譯時發生的,而不是在運行時。這意味著,修飾器能在編譯階段運行代碼。也就是說,修飾器本質就是編譯時執行的函數。
裝飾器只會在程序第一次運行時執行一次,裝飾的代碼會被返回的值代替。
# 裝飾器應用
@Decorator的使用方法
裝飾器應用,目前支持以下幾處使用:
* Class
* 函數
* get set訪問器
* 實例屬性、靜態函數及屬性
* 函數參數
@Decorator的語法規定比較簡單,就是通過`@`符號后邊跟一個裝飾器函數的引用:
編寫一個記錄構造函數參數:
```
function log(Class) {
return (...args) => { //根據傳入的參數需要返回一個函數
console.log(args);
return new Class(...args);
};
}
```
這里接收一個類作為參數,返回新函數作為構造器。此函數打印出參數,返回這些參數構造的實例。
例如:
```
@log
class Example {
constructor(name, age) {
}
}
const e = new Example('Graham', 34);
// [ 'Graham', 34 ]
console.log(e);
// Example {}
```
構造`Example`類時會輸出提供的參數,構造值`e`也確實是`Example`的實例。
傳遞參數到類裝飾器與類成員一樣。
## 類屬性裝飾器
屬性裝飾器適用于類的單獨成員-無論是屬性、方法、get函數或set函數。
裝飾器函數調用三個參數:
- target-被修飾的類
- name-類成員的名字
- descriptor-成員描述符。對象會將這個參數傳給Object.defineProperty
`@readonly`是經典的例子:
```
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
```
上例會將成員描述符中的`writable`設為`false`。
接著用于類中屬性:
```
class Example {
a() {}
@readonly
b() {}
}
const e = new Example();
e.a = 1;
e.b = 2;
// TypeError: Cannot assign to read only property 'b' of object '#<Example>'
```
## 修飾器不能用于函數
```
var readOnly = require("some-decorator");
@readOnly function foo() {
}
```
上面的代碼也有問題,因為實際執行的代碼如下。
```
var readOnly;
@readOnly function foo() {}
readOnly =require("some-decorator");
```
總之,由于存在函數提升,修飾器不能用于函數。類是不會提升的,所以就沒有這方面的問題。另一方面,如果一定要修飾函數,可以采用高階函數的形式直接執行。
```
function doSomething(name) {
console.log ( 'Hello,' + name);
}
function loggingDecorator(wrapped) {
return function() {
console.log( 'Starting') ;
const result = wrapped.apply(this, arguments);
console.log( 'Finished' ) ;
return result;
}
}
const wrapped= loggingDecorator(doSomething);
```
# 參考
ES6標準-阮一峰