[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标准-阮一峰