Temporary interest, want to write an article about ts decorator, spent half a day sorting out..
This stuff, in ES2017, seems to have... Documentation. Here.
Because temporarily, I don't want to write too many text introductions. Just open the code with a little text description.
This article explains what the decorator is through the decorator code compiled by ts. What can we do? What are the benefits?
Implementation code
The code after compilation is like this, with comments:
var __decorate = (this && this.__decorate) || function(decorators, target, key, desc) { // c parameter length // R? C < 3 is the target, otherwise if desc is null, desc will take the description of the key attribute of the target, or else desc will be the desc // d Reserved for Use var c = arguments.length, r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc, d; // The following text explains that this is just a throwing pot. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); // Loop decorators and assign values to d each time, and determine values else for (var i = decorators.length - 1; i >= 0; i--) if ((d = decorators[i])) // C < 3, using r as the input of decorators[i]; // C > 3, target, key, r are executed as decorators[i]; // C=== 3, target, key is executed as input to decorators[i]. // If the execution does not return a result, r = r;. r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; // If C > 3 & & r, modify the target and return R return c > 3 && r && Object.defineProperty(target, key, r), r; };
As you can see from the code, the end result is either to use decorator to execute the target to change something; or to use Object.defineProperty to operate on the target, the code is just a few lines, which is of great use. The specific execution process will be easier to understand in combination with the following two examples.
It's worth mentioning that the Reflect in the code was supposed to be this sec-reflect-object But unfortunately not.
Then guess it's the implementation of Typescript, flip Typescript/tsc.js The code (see node_modules if you can't open the links) doesn't work either. Look at stackoverflow's explanation again. what-is-reflect-decorate-in-js-code-transpiled-from-ts
Generally speaking, ts wants to throw the pot to ES to make up for it. Then the code in ts's else is polyfill.
case
The following decorator s and class es are illustrated as examples
// ts code function show(target: any) { console.log(target); target.prototype.showMe = (name: string) => { console.log("show me :", name); }; } interface IShow { showMe?(name: string): any; } @show class Show implements IShow { showMe(name: string) {} } const shoow = new Show(); shoow.showMe("ys"); // Compiled js // Decrator, simple printing, and modifying methods function show(target) { console.log(target); target.prototype.showMe = function(name) { console.log("show me :", name); }; } // class Shoow var Shoow = (function() { function Shoow() {} Shoow.prototype.showMe = function(name) {}; // Decrators are [show], target is Shoow Shoow = __decorate([show], Shoow); return Shoow; })(); var shooow = new Shoow(); shooow.showMe("ys"); // output : show me : ys
Understand the implementation steps:
- decorators = [show],target = Shoow,
- c = 2,r = target{Shoow},d = undefined
- There is no Reflect, go through the loop, only cycle once
- d = show, r = show(target{Shoow}), r does not return the result, so r is still r, r = target{Shoow}
- Return result: c = 2, so return false
- There is no return value after execution, but the showMe method is changed when the show(target{Shoow}) is executed, and the execution results are in line with expectations.
One is not enough? One more? Try returning a function in it this time?
// ts code function logger1(config?) { return function(target, key: string, descriptor: PropertyDescriptor) { const _value = descriptor.value; if (typeof _value === "function") { descriptor.value = (...args) => { console.log(`logger1-begin : ${config.level}`); const res = _value.apply(target, args); console.log(`logger1-end`); return res; }; } return descriptor; }; } function logger2(config?) { return function(target, key: string, descriptor: PropertyDescriptor) { const _value = descriptor.value; if (typeof _value === "function") { descriptor.value = (...args) => { console.log(`logger2-begin : ${config.level}`); const res = _value.apply(target, args); console.log(`logger2-end`); return res; }; } return descriptor; }; } interface IShow { showMe?(name: string): any; } class Show implements IShow { @logger1({ level: "info" }) @logger2({ level: "error" }) showMe(name: string) { console.log("show me :", name); } } const shoow = new Show(); shoow.showMe("ys"); // The output is manually indented, and the showMe method has been wrapped several times. // logger1-begin : info // logger2-begin : error // show me : ys // logger2-end // logger1-end
Look again at the implementation steps:
- Decrators = logger1, logger2], target = Shoow, key = showMe, desc = null Note that this is for null, not undefined
- c = 4,r = target{Shoow},d = undefined
- There is no Reflect, go through the loop, only cycle once
- The first loop takes d = logger1, r = logger1(target, key, r), because return has a value, r = logger1's return function, then descriptor.value is rewritten for the first time.
- The second loop takes d = logger2, r = logger2(target, key, r), and because return has a value, r = logger2's return function, then descriptor.value is rewritten for the second time.
- Return result: Because C > 3, r has a value, Object.defineProperty(target, key, r) is executed to override the object property and return r (r is the result of rewriting).
- After two overrides of showMe attribute values, the execution results are in line with expectations.
Happy
What joy does the ornament bring you? Simply list some of the most obvious advantages, the rest in the use of their own extract the daily happiness to go to it.........................................
- Decoupling between business and functionality (such as logging)
// daily dosomething(){ consol.log('start') // some thing console.log('end') } // Use decorators @logger(logConfig?) dosomething();
- Clear code structure (especially for React components after multi-tier HOC)
// Daily Multilayer HOC class MyComponent extends Component{ // .. } connect(mapFn)( MyHoc(someMapFn)( Form.create(fieldsMapFn)(MyComponent) ) ) // Use decorators @connect(mapFn) @MyHoc(someMapFn) @FormFields(fieldsMapFn) class MyComponent extends Component{ // .. } export default MyComponent;
Last
AOP, get to know