Written in the beginning
- ES6 common but ignored methods series of articles, sorting out the author thinks that some daily development may use some methods, use skills and some application scenarios, details please see related content links, welcome to supplement exchange.
Related articles
- Common but overlooked methods for ES6 (Destruct assignments and values first)
- ES6 Common but ignored methods (second bullet functions, arrays, and objects)
- ES6 common but ignored methods (third bullet Symbol, Set, and Map)
- ES6 commonly used but overlooked methods (fourth bullet Proxy and Reflect)
- ES6 common but ignored methods (# 5 Promise and Iterator)
- Common but ignored methods for ES6 (Generator 6)
- ES6 Common but ignored methods (async)
- ES6 Common but ignored methods (eighth bullet Class)
- ES6 common but ignored methods (Module 9)
- Common but ignored methods of ES6 (Development specification of the Tenth Bullet Project)
- Common but overlooked approaches to ES6 (End game – Latest Proposal)
Decorator
- ES6-Decorator
Environment configuration
- Because the decorator is currently in
stage 2
, so it can’t be used directly in the browser, we need to passbabel
Compiled intoes5
To use. - If we just want to use this syntax to do a simple understanding, we can simply configure it as follows:
- Global installation
@babel/cli
and@babel/core
.
npm install -g @babel/core @babel/cli Copy the code
- Initializes one in the project directory
package.json
And installdecorator
The relevantbabel
The plug-in@babel/plugin-proposal-class-properties
and@babel/plugin-proposal-decorators
.
Json NPM install -- save-dev@babel /plugin-proposal-class-properties NPM install --save-dev @babel/plugin-proposal-decoratorsCopy the code
- Create in the project root directory
.babelrc
File, and add some code.
{ "presets": [], "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ], [ "@babel/plugin-proposal-class-properties", { "loose": true } ] ] } Copy the code
- in
package.json
thescripts
To add the compile command.
// package.json // Babel inputFile -w (live compilation) -o (output) outputFile scripts: {build: 'Babel es6.js -w -o es5.js'}Copy the code
- in
html
The compiled file is imported into the file.
// index.html <DOCTYPE html> <html> ... <body> ... <script src='./es5.js'></script> </body> </html> Copy the code
- Global installation
introduce
Decorator
The proposal has been greatly revised and has not yet been finalized. I wonder if the grammar will change again.A decorator (Decorator
) is a class associated with (class
Syntax for annotating or modifying classes and class methods@ +
The function name. It can precede the definition of classes and class methods.
@frozen class Foo {
@configurable(false)
@enumerable(true)
method() {}
@throttle(500)
expensiveMethod() {}
}
Copy the code
use
The adornment of the class
- Decorators can decorate an entire class. A decorator is a function that handles a class. The first argument to the decorator function is the target class to decorate.
@ decorator class A {} / / is equivalent to A class A {} A = decorator (A) | | A;Copy the code
- If one argument is not enough, you can wrap another function around the decorator.
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
Copy the code
- Decorators change the behavior of classes at compile time, not run time. This means that the decorator can run the code at compile time. That is, decorators are essentially functions that are executed at compile time.
- To add instance attributes, go to the target class’s
prototype
Object operation.
function testable(target) {
target.prototype.isTestable = true;
}
@testable
class MyTestableClass {}
let obj = new MyTestableClass();
obj.isTestable // true
Copy the code
- Decorator function
testable
It’s in the target classprototype
Object, so you can call it on the instance.
Method decoration
- Decorators can decorate not only classes but also class properties. For example: decorators
readonly
Used to decorate the classgetName
Methods. Decorator functionreadonly
There are three parameters that can be accepted,target
(object of decoration),name
(Decorator property name),descriptor
: Attribute description.
class Person { @readonly name() { return `${this.first} ${this.last}` } } function readonly(target, name, Descriptor){console.log(target, name, descriptor) // Descriptor Object original value: // {// value: specifiedFunction, // enumerable: false, // configurable: true, // writable: true // }; descriptor.writable = false; // Set not to write return descriptor; Person.prototype.getName = function(name) {console.log(name)} const per = new Person() console.log('getName', per.getName('detanx'))Copy the code
- If the same method has more than one decorator, it will be executed from the outside in and from the inside out, like peeling an onion.
function dec(id){
console.log('evaluated', id);
return (target, property, descriptor) => console.log('executed', id);
}
class Example {
@dec(1)
@dec(2)
method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1
Copy the code
Why can’t decorators be used for functions?
- Decorators can only be used for classes and methods of classes, not functions, because function promotion exists.
var counter = 0;
var add = function () {
counter++;
};
@add
function foo() {
}
Copy the code
- The above code is intended to be executed after
counter
Is equal to the1
But the actual result iscounter
Is equal to the0
. Because the function is promoted, the actual code executed looks like this.
@add
function foo() {
}
var counter;
var add;
counter = 0;
add = function () {
counter++;
};
Copy the code
- Another example.
var readOnly = require("some-decorator");
@readOnly
function foo() {
}
Copy the code
- The above code is also problematic because the actual execution is as follows.
var readOnly;
@readOnly
function foo() {
}
readOnly = require("some-decorator");
Copy the code
- If you must decorate a function, you can execute it directly as a higher-order function.
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);
Copy the code
core-decorators.js
core-decorators.js
Decorators is a third-party module that provides several common decorators to better understand decorators.
@autobind
autobind
Decorator makes the method inthis
Object to bind the original object.
import { autobind } from 'core-decorators';
class Person {
@autobind
getPerson() {
return this;
}
}
let person = new Person();
let getPerson = person.getPerson;
getPerson() === person;
// true
Copy the code
@readonly
readonly
Decorators make properties or methods unwritable.
import { readonly } from 'core-decorators';
class Meal {
@readonly
entree = 'steak';
}
var dinner = new Meal();
dinner.entree = 'salmon';
// Cannot assign to read only property 'entree' of [object Object]
Copy the code
@override
override
The decorator checks if a subclass’s method overrides a parent class’s method of the same name and raises an error if it does not.
import { override } from 'core-decorators';
class Parent {
speak(first, second) {}
}
class Child extends Parent {
@override
speak() {}
// SyntaxError: Child#speak() does not properly override Parent#speak(first, second)
}
// or
class Child extends Parent {
@override
speaks() {}
// SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain.
// Did you mean "speak"?
}
Copy the code
@deprecate (alias @deprecated)
deprecate
ordeprecated
The decorator displays a warning on the console indicating that the method will be abolished.
import { deprecate } from 'core-decorators';
class Person {
@deprecate
facepalm() {}
@deprecate('We stopped facepalming')
facepalmHard() {}
@deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' })
facepalmHarder() {}
}
let person = new Person();
person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.
person.facepalmHard();
// DEPRECATION Person#facepalmHard: We stopped facepalming
person.facepalmHarder();
// DEPRECATION Person#facepalmHarder: We stopped facepalming
// See http://knowyourmeme.com/memes/facepalm for more details.
Copy the code
@suppressWarnings
suppressWarnings
Decorator suppressiondeprecated
Decoratorconsole.warn()
The call. Exceptions are, however, calls made by asynchronous code.
import { suppressWarnings } from 'core-decorators';
class Person {
@deprecated
facepalm() {}
@suppressWarnings
facepalmWithoutWarning() {
this.facepalm();
}
}
let person = new Person();
person.facepalmWithoutWarning();
// no warning is logged
Copy the code
Mixin
- On the basis of decorator, can be implemented
Mixin
Mode.Mixin
Schema is an alternative to object inheritance.mix in
), means to mix one object with another object’s methods. - Deploy a generic script
mixins.js
That will beMixin
Write it as a decorator.
function
export function mixins(... list) { return function (target) { Object.assign(target.prototype, ... list); }; }Copy the code
- use
mixins
This decorator “blends” various methods for classes.
import { mixins } from './mixins';
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // "foo"
Copy the code
Class implements
- I’ll rewrite the function
MyClass
Of the classprototype
Objects, if you don’t like this, can also be implemented through class inheritanceMixin
.
class MyClass extends MyBaseClass {
/* ... */
}
Copy the code
MyClass
inheritedMyBaseClass
. If we want to be inMyClass
One of them was “mixed in”foo
The way. One way is inMyClass
andMyBaseClass
Insert a mixin class withfoo
Method, and inheritsMyBaseClass
And thenMyClass
And then inherits that class.
let MyMixin = (superclass) => class extends superclass { foo() { console.log('foo from MyMixin'); }};Copy the code
MyMixin
Is a mixin class generator, acceptsuperclass
As an argument, and then returns an inheritancesuperclass
Class, which contains onefoo
Methods. Then, the target class inherits the mixin class, reaching mixin.foo
The purpose of the method. If you need to “mix” more than one method, generate more than one blend class.
class MyClass extends MyMixin(MyBaseClass) { /* ... */ } let c = new MyClass(); c.foo(); MyClass extends Mixin1(Mixin2(MyBaseClass)) {/*... * /}Copy the code
- One of the nice things about this is that you can call it
super
, so you can avoid overwriting methods of the same name in the parent class in the mixin process.
let Mixin1 = (superclass) => class extends superclass { foo() { console.log('foo from Mixin1'); if (super.foo) super.foo(); }}; let Mixin2 = (superclass) => class extends superclass { foo() { console.log('foo from Mixin2'); if (super.foo) super.foo(); }}; class S { foo() { console.log('foo from S'); } } class C extends Mixin1(Mixin2(S)) { foo() { console.log('foo from C'); super.foo(); }}Copy the code
- Code, each time mixin occurs, calls the parent class’s
super.foo
Method, causing the parent’s method of the same name not to be overridden and the behavior to be preserved.
new C().foo()
// foo from C
// foo from Mixin1
// foo from Mixin2
// foo from S
Copy the code
Trait
-
Traits are also decorators that have similar effects to mixins but provide more functionality, such as preventing conflicts between methods with the same name, excluding methods from being mixed in, aliasing methods that are mixed in, and so on.
-
Take the traits-decorator third-party module as an example. This module provides traits decorators that accept not only objects but also ES6 classes as parameters.
import { traits } from 'traits-decorator';
class TFoo {
foo() { console.log('foo') }
}
const TBar = {
bar() { console.log('bar') }
};
@traits(TFoo, TBar)
class MyClass { }
let obj = new MyClass();
obj.foo() // foo
obj.bar() // bar
Copy the code
- In the code above, pass
traits
Decorator, inMyClass
Class “mixed” aboveTFoo
Of the classfoo
Methods andTBar
The object’sbar
Methods. Trait
Methods of the same name are not allowed to “blend in”.
import { traits } from 'traits-decorator'; class TFoo { foo() { console.log('foo') } } const TBar = { bar() { console.log('bar') }, foo() { console.log('foo') } }; @traits(TFoo, TBar) class MyClass {} // throw new Error('Method named: '+ methodName +' is defined twice.'); // ^ // Error: Method named: foo is defined twice.Copy the code
- In the code above,
TFoo
andTBar
There arefoo
Method, resulttraits
The decorator reported an error. - One solution is elimination
TBar
thefoo
Methods.
import { traits, excludes } from 'traits-decorator'; class TFoo { foo() { console.log('foo') } } const TBar = { bar() { console.log('bar') }, foo() { console.log('foo') } }; @traits(TFoo, TBar::excludes('foo')) class MyClass { } let obj = new MyClass(); obj.foo() // foo obj.bar() // barCopy the code
- The above code uses the binding operator (
: :
) inTBar
Ruled out onfoo
Method will not report errors when mixed. - Another way is to do it
TBar
thefoo
Give the method an alias.
import { traits, alias } from 'traits-decorator'; class TFoo { foo() { console.log('foo') } } const TBar = { bar() { console.log('bar') }, foo() { console.log('foo') } }; @traits(TFoo, TBar::alias({foo: 'aliasFoo'})) class MyClass { } let obj = new MyClass(); obj.foo() // foo obj.aliasFoo() // foo obj.bar() // barCopy the code
-
The code above aliases TBar’s foo method, so MyClass can be mixed in with TBar’s foo method.
-
The Alias and Excludes method can be used in combination.
@traits(TBar::excludes('foo','bar')::alias({baz:'tBar'}))
class MyClass {}
Copy the code
- The code above rules it out
TBar
thefoo
Methods andbar
Methods forbaz
The method is aliasedtBaz
. as
Method provides another way to write the code above.
@traits(TBar::as({excludes:['foo', 'bar'], alias: {baz: 'exampleBaz'}}))
class MyClass {}
Copy the code