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