如何实现打印机装饰?
TypeScript 1.5现在有装饰器 。
有人可以提供一个简单的例子,说明实现装饰器的正确方法,并描述可能有效的装饰器签名中的参数是什么意思?
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void; declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void; declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;
另外,在实现一个装饰器的时候,有没有最好的实践考虑因素呢?
我结束了与装饰者玩耍,并决定logging我想要的任何人谁想要利用这之前,任何文件出来。 如果您发现任何错误,请随时编辑。
一般要点
- 当声明类时调用装饰器,而不是在实例化对象时调用装饰器。
- 可以在同一个Class / Property / Method / Parameter上定义多个装饰器。
- 装饰者不允许在构造函数上使用。
一个有效的装饰者应该是:
- 可分配给其中一个装饰器types(
ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator
)。- 返回可分配给装饰值的值(在类装饰器和方法装饰器的情况下)。
参考
方法/正式Accessor装饰
执行参数:
-
target
:类(Object
)的原型。 -
propertyKey
:方法的名称(string
|symbol
)。 -
descriptor
:TypedPropertyDescriptor
– 如果您不熟悉描述符的键,我build议在Object.defineProperty
(这是第三个参数)的文档中阅读它。
示例 – 没有参数
使用:
class MyClass { @log myMethod(arg: string) { return "Message -- " + arg; } }
执行:
function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) { const originalMethod = descriptor.value; // save a reference to the original method // NOTE: Do not use arrow syntax here. Use a function expression in // order to use the correct value of `this` in this method (see notes below) descriptor.value = function(...args: any[]) { // pre console.log("The method args are: " + JSON.stringify(args)); // run and store result const result = originalMethod.apply(this, args); // post console.log("The return value is: " + result); // return the result of the original method (or modify it before returning) return result; }; return descriptor; }
input:
new MyClass().myMethod("testing");
输出:
方法参数是:[“testing”]
返回值是:消息 – testing
笔记:
- 设置描述符的值时不要使用箭头语法。 如果你这样做的话,上下文不会是实例。
- 通过返回一个新的描述符来修改原始描述符比覆盖当前描述符更好。 这允许你使用多个装饰器来编辑描述符,而不会覆盖另一个装饰器所做的事情。 这样做可以让你同时使用类似
@enumerable(false)
和@log
东西(Example: Bad vs Good ) - 有用的 :
TypedPropertyDescriptor
的type参数可以用来限制修饰器可以放置哪些方法签名( 方法示例 )或访问器签名( Accessor Example )。
示例 – 使用参数(装饰工厂)
当使用参数时,你必须声明一个带有装饰器参数的函数,然后返回一个带有没有参数的例子的签名的函数。
class MyClass { @enumerable(false) get prop() { return true; } } function enumerable(isEnumerable: boolean) { return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => { descriptor.enumerable = isEnumerable; return descriptor; }; }
静态方法装饰器
类似于装饰器的方法有一些不同之处:
- 它的
target
参数是构造函数本身而不是原型。 - 描述符是在构造函数中定义的,而不是原型。
类装饰
@isTestable class MyClass {}
执行参数:
-
target
:装饰器声明的类(TFunction extends Function
)。
使用示例 :使用元数据api将信息存储在类中。
属性装饰
class MyClass { @serialize name: string; }
执行参数:
-
target
:类(Object
)的原型。 -
propertyKey
:propertyKey
的名称(string
|symbol
)。
使用示例 :创build一个@serialize("serializedName")
装饰器,并将属性名称添加到要序列化的属性列表中。
参数装饰器
class MyClass { myMethod(@myDecorator myParameter: string) {} }
执行参数:
-
target
:类的原型(Function
– 看起来Function
不再起作用了,现在你应该使用any
或者Object
来使用任何类中的装饰器,或者指定要限制它的类的types至) -
propertyKey
:方法的名称(string
|symbol
)。 -
parameterIndex
:函数参数列表中的参数索引(number
)。
简单的例子
详细示例(s)
- Memoize装饰器 – 方法,获取/设置访问器装饰器的例子
class Foo { @consoleLogger Boo(name:string) { return "Hello, " + name } }
- 目标:在上述案例中,该类的原型是“Foo”
- propertyKey:所调用方法的名称,在上面的例子中“Boo”
- 描述符:描述对象=>包含值属性,这又是函数本身:function(name){return'Hello'+ name; }
您可以实现将每个调用logging到控制台的内容:
function consoleLogger(target: Function, key:string, value:any) { return value: (...args: any[]) => { var a = args.map(a => JSON.stringify(a)).join(); var result = value.value.apply(this, args); var r = JSON.stringify(result); console.log('called method' + key + ' with args ' + a + ' returned result ' + r); return result; } }
在其他答案中我没有看到一件重要的事情:
装饰工厂
如果我们想定制一个装饰器如何应用于一个声明,我们可以写一个装饰器工厂。 装饰器工厂只是一个函数,返回装饰器在运行时将调用的expression式。
// This is a factory, returns one of ClassDecorator, // PropertyDecorator, MethodDecorator, ParameterDecorator function Entity(discriminator: string): { return function(target) { // this is the decorator, in this case ClassDecorator. } } @Entity("cust") export class MyCustomer { ... }
检查TypeScript手册装饰者一章 。