definition
Decorators are special types of declarations that can be attached to class declarations, methods, accessors, properties, or parameters
It is a function of dynamically extending objects without changing the original class and using inheritance
Similarly, in essence, it is not a tall structure, but an ordinary function. The form of @ expression is actually the syntax of Object.defineProperty
expression must also be a function after evaluation. It will be called at runtime and the decorated declaration information will be passed in as parameters
Mode of use
Since typescript is an experimental feature, to use it, you need to start it in the tsconfig.json file, as follows:
{ "compilerOptions": { "target": "ES5", "experimentalDecorators": true } }
The use of typescript decorator is basically the same as that of javascript
The decorator of class can decorate:
class
Method / attribute
parameter
Accessor
Class decoration
For example, declare a function addAge to add age to the attribute age of Class
function addAge(constructor: Function) { constructor.prototype.age = 18; } @addAge class Person{ name: string; age!: number; constructor() { this.name = 'huihui'; } } let person = new Person(); console.log(person.age); // 18
The above code is actually equivalent to the following form:
Person = addAge(function Person() { ... });
As can be seen from the above, when the decorator is used as a modifier class, the constructor will be passed in. constructor.prototype.age is to add an age attribute to each instantiated object
Method / attribute decoration
Similarly, the decorator can be used to decorate the method of the class. At this time, the parameters received by the decorator function become:
target: the prototype of the object
propertyKey: name of the method
Descriptor: Property descriptor of the method
You can see that these three properties are actually the three parameters of Object.defineProperty. If they are class properties, the third parameter is not passed
Examples are as follows:
//Declare decorator modifier methods / properties
function method(target: any, propertyKey: string, descriptor: PropertyDescriptor) { console.log(target); console.log("prop " + propertyKey); console.log("desc " + JSON.stringify(descriptor) + "\n\n"); descriptor.writable = false; }; function property(target: any, propertyKey: string) { console.log("target", target) console.log("propertyKey", propertyKey) } class Person{ @property name: string; constructor() { this.name = 'huihui'; } @method say(){ return 'instance method'; } @method static run(){ return 'static method'; } } const xmz = new Person(); // Modify instance method say xmz.say = function() { return 'edit' }
The output is shown in the following figure:
Parameter decoration
Receive three parameters, namely:
target: the prototype of the current object
propertyKey: name of the parameter
index: position in parameter array
function logParameter(target: Object, propertyName: string, index: number) { console.log(target); console.log(propertyName); console.log(index); } class Employee { greet(@logParameter message: string): string { return `hello ${message}`; } } const emp = new Employee(); emp.greet('hello');
The input is shown in the figure below:
Accessor decoration
The use method is consistent with the decoration method, as follows:
function modification(target: Object, propertyKey: string, descriptor: PropertyDescriptor) { console.log(target); console.log("prop " + propertyKey); console.log("desc " + JSON.stringify(descriptor) + "\n\n"); }; class Person{ _name: string; constructor() { this._name = 'huihui'; } @modification get name() { return this._name } }
Decorator factory
If you want to pass parameters to turn the decorator into a factory like function, you only need another function inside the decorator function, as follows:
function addAge(age: number) { return function(constructor: Function) { constructor.prototype.age = age } } @addAge(10) class Person{ name: string; age!: number; constructor() { this.name = 'huihui'; } } let person = new Person();
Execution sequence
When multiple decorators are applied to a declaration, the decorator expression will be evaluated from top to bottom, and the evaluation result will be called from bottom to top as a function, for example:
function f() { console.log("f(): evaluated"); return function (target, propertyKey: string, descriptor: PropertyDescriptor) { console.log("f(): called"); } } function g() { console.log("g(): evaluated"); return function (target, propertyKey: string, descriptor: PropertyDescriptor) { console.log("g(): called"); } } class C { @f() @g() method() {} } // output f(): evaluated g(): evaluated g(): called f(): called
Application scenario
It can be seen that there are two significant advantages of using decorators:
The code becomes more readable, and the decorator name is equivalent to a comment
Extend the original function without changing the original code
In the following usage scenarios, with the help of the characteristics of the decorator, in addition to improving readability, for existing classes, the original functions can be extended without changing the original code through the characteristics of the decorator