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 instage 2, so it can’t be used directly in the browser, we need to passbabelCompiled intoes5To use.
  • If we just want to use this syntax to do a simple understanding, we can simply configure it as follows:
    1. Global installation@babel/cliand@babel/core.
    npm install -g @babel/core @babel/cli
    Copy the code
    1. Initializes one in the project directorypackage.jsonAnd installdecoratorThe relevantbabelThe plug-in@babel/plugin-proposal-class-propertiesand@babel/plugin-proposal-decorators.
    Json NPM install -- save-dev@babel /plugin-proposal-class-properties NPM install --save-dev @babel/plugin-proposal-decoratorsCopy the code
    1. Create in the project root directory.babelrcFile, and add some code.
    {
      "presets": [],
      "plugins": [
        [
          "@babel/plugin-proposal-decorators",
          {
            "legacy": true
          }
        ],
        [
          "@babel/plugin-proposal-class-properties",
          {
            "loose": true
          }
        ]
      ]
    }
    Copy the code
    1. inpackage.jsonthescriptsTo 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
    1. inhtmlThe compiled file is imported into the file.
    // index.html
    <DOCTYPE html>
    <html>
    ...
    <body>
    ...
    <script src='./es5.js'></script>
    </body>
    </html>
    Copy the code

introduce

  • DecoratorThe 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 (classSyntax 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’sprototypeObject operation.
function testable(target) {
  target.prototype.isTestable = true;
}

@testable
class MyTestableClass {}

let obj = new MyTestableClass();
obj.isTestable // true
Copy the code
  • Decorator functiontestableIt’s in the target classprototypeObject, so you can call it on the instance.
Method decoration
  • Decorators can decorate not only classes but also class properties. For example: decoratorsreadonlyUsed to decorate the classgetNameMethods. Decorator functionreadonlyThere 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 aftercounterIs equal to the1But the actual result iscounterIs 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.jsDecorators is a third-party module that provides several common decorators to better understand decorators.
@autobind
  • autobindDecorator makes the method inthisObject 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
  • readonlyDecorators 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
  • overrideThe 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)
  • deprecateordeprecatedThe 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
  • suppressWarningsDecorator suppressiondeprecatedDecoratorconsole.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 implementedMixinMode.MixinSchema is an alternative to object inheritance.mix in), means to mix one object with another object’s methods.
  • Deploy a generic scriptmixins.jsThat will beMixinWrite it as a decorator.
function
export function mixins(... list) { return function (target) { Object.assign(target.prototype, ... list); }; }Copy the code
  • usemixinsThis 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 functionMyClassOf the classprototypeObjects, if you don’t like this, can also be implemented through class inheritanceMixin.
class MyClass extends MyBaseClass {
  /* ... */
}
Copy the code
  • MyClassinheritedMyBaseClass. If we want to be inMyClassOne of them was “mixed in”fooThe way. One way is inMyClassandMyBaseClassInsert a mixin class withfooMethod, and inheritsMyBaseClassAnd thenMyClassAnd then inherits that class.
let MyMixin = (superclass) => class extends superclass { foo() { console.log('foo from MyMixin'); }};Copy the code
  • MyMixinIs a mixin class generator, acceptsuperclassAs an argument, and then returns an inheritancesuperclassClass, which contains onefooMethods. Then, the target class inherits the mixin class, reaching mixin.fooThe 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 itsuper, 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’ssuper.fooMethod, 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, passtraitsDecorator, inMyClassClass “mixed” aboveTFooOf the classfooMethods andTBarThe object’sbarMethods.
  • TraitMethods 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,TFooandTBarThere arefooMethod, resulttraitsThe decorator reported an error.
  • One solution is eliminationTBarthefooMethods.
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 (: :) inTBarRuled out onfooMethod will not report errors when mixed.
  • Another way is to do itTBarthefooGive 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 outTBarthefooMethods andbarMethods forbazThe method is aliasedtBaz.
  • asMethod provides another way to write the code above.
@traits(TBar::as({excludes:['foo', 'bar'], alias: {baz: 'exampleBaz'}}))
class MyClass {}
Copy the code