TypeScript装饰器+源数据编程:从新手到专家Part1
- 原文链接
- 本文只从阅读原文中提取部分内容,方便临时查阅
- MAY 18, 2015为原文发布日期,部分信息可能已经不再适用,但是基本原理依然没变,需要仔细研读。
A few months ago Microsoft and Google announced that they were working together on TypeScript and Angular 2.0
We’re excited to announce that we have converged the TypeScript and AtScript languages, and that Angular 2, the next version of the popular JavaScript library for building web sites and web apps, will be developed with TypeScript.
brief
This series will cover:
PART I: Method decorators
PART II: Property decorators & Class decorators
PART III: Parameter decorators & Decorator factory
PART IV: Types serialization & The metadata reflection API
The difference between Annotations and Decorators
- Annotation and decorator are pretty much the same:
Annotations and decorators are nearly the same thing.
From a consumer perspective we have exactly the same syntax.
The only thing that differs is that we don’t have control over how annotations are added as meta data to our code.
Whereas decorators is rather an interface to build something that ends up as annotation.Over a long term, however, we can just focus on decorators, since those are a real proposed standard. AtScript is TypeScript and TypeScript implements decorators.
Let’s take a look to the TypeScript’s decorators syntax.
Note: If you want to learn more about the difference between Annotations and Decorators there is a great article by Pascal Precht on this topic.
Decorators in TypeScript
In the TypeScript source code we can find the signature of the available types of decorators:
|
Method decorators
- To invoke a method decorator we need to prefix the method that we wish to decorate with the @ character follow by the name of the decorator. In the case of a decorator named log, the syntax will look as follows:
|
- Before we can actually use @log we need to declare the method decorator somewhere in our application. Let’s take a look to the log method decorator implementation.
|
Note: Please take a look to updates at the end of this post for an alternative implementation, which a avoids one potential issue.
A method decorators takes a 3 arguments:
target the method being decorated.
key the name of the method being decorated.
value a property descriptor of the given property if it exists on the object, undefined otherwise. - The property descriptor is obtained by invoking the Object.getOwnPropertyDescriptor() function.
There is something strange right? We didn’t pass any of these parameters when we used the decorator @log in the C class definition. At this point we should be wondering who is providing those arguments? and Where is the
log
method being invoked?We can find the answers to these questions by examining the code that the TypeScript compiler will generate for the code above.
|
- Without the @log decorator the generated JavaScript for the C class would just be as follows.
|
- But when we add the
@log
decorator the following additional code is added to the class definition by the TypeScript compiler.
|
If we read the MDN documentation we will learn that the following about the defineProperty
function.
The Object.defineProperty() method defines a new property directly on an object,
or modifies an existing property on an object, and returns the object.
The TypeScript compiler is passing the prototype of C, the name of the method being decorated (
foo
) and the return of a function named__decorate
to thedefineProperty
method.The TypeScript compiler is using the
defineProperty
method to override the method being decorated. The new method implementation will be the value returned by the function__decorate
. By now we should have a new question: Where is the__decorate
function declared?
|
- In a similar manner, when we use a decorator a function named
__decorator
is generated by the TypeScript compiler. Let’s take a look to the__decorator
funcion.
|
- The first line in the code snippet above is using an OR operator to ensure that if the function
__decorator
is generated more than once it will not be override again and again. In the second line, we can observe a conditional statement:
|
The conditional statement is used to detect an upcoming JavaScript feature: The metadata reflection API.
Note: We will focus on the metadata reflection API towards the end of this post series so let’s ignore it for now.
Let’s remember how did we get here for a second. The method foo is about to be override by the return of the function
__decorate
which was invoked with the following parameters.
|
We are now inside the __decorate
method and because the metadata reflection API is not available, a fallback is about to be executed.
|
Because 4 parameters are passed to the __decorate
method, the case 4 will be executed. Understanding this piece of code can be a challenge because the name of the variables are not really descriptive but we are not scared of it right?
Let’s start by learning about the reduceRight
method.
The reduceRight method applies a function against an accumulator and each value of the array (from right-to-left) has to reduce it to a single value.
- The code below performs the exact same operation but it had been rewritten to facilitate its understanding
|
When the code above is executed the decorator log
is invoked and we can see that some parameters are passed to it: C.prototype
,”foo
“ and previousValue
. So we have finally answered our original questions:
- Who is providing those arguments?
- Where is the
log
method being invoked?
If we return to the log
decorator implementation we will be able to understand much better what happens when it is invoked.
|
- After decorating the
foo
method it will continue to work as usually but it will also execute the extra logging functionality added by thelog
the decorator.
|
Conclusions
It has been a journey right? I hope you have enjoyed as much as I have. We are just getting started but we already know enough to create some truly awesome stuff.
Method decorators can be used for many interesting features. For example, If you have ever worked with spies in testing frameworks like SinonJS you will probably get excited when you realize that decorators are going to allow us to do things like create spies by just adding a @spy decorator.
In the next chapter of this series we will learn how to work with Property decorators. Don’t forget to subscribe if you don’t want to miss it out!
总结:装饰器从某种意义上讲就是一种“注解”机制的实现,对于angular中的“注解”而言,他是一种被指定了特殊编译过程的装饰器。明白了装饰器的原理,也就能够理解注解对于angular框架来说的意义。再说TS中装饰器,如果把其他框架结合TS使用 ,那么TS中的注解,从某种意义上来说,就是如同angular注解一样,是一种注解AOP编程思维的转换。