The class definition

ECMAScript has no formal class type. Classes are the new basic syntactic sugar structure in ECMAScript, with the concepts of stereotypes and constructors still behind them.

/ / the class declaration
class Person {}
// Class expression
const Animal = class {} 
/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- need to pay attention to the details of the -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

//1. Class has no variable promotion
console.log(Person)
class Person {}// Uncaught ReferenceError: Cannot access 'Person' before initialization

//2. Classes are block-scoped
{
    class ClassDeclaration {}}console.log(ClassDeclaration); // ReferenceError: ClassDeclaration is not defined

//3. The class cannot be configured, enumerated, or overridden
class Person {}
console.log(Object.getOwnPropertyDescriptor(Person, 'prototype'))Value: / / {{... }, writable: false, enumerable: false, configurable: false}

//4.Class defaults to strict mode
Copy the code

Class is a special kind of function in many ways.

class Person {}
// Check that Person is a function type using the typeof operator.
console.log(Person) // class Person {}
console.log(typeof Person) // function
// Person has a prototype attribute, and the prototype object has a constructor attribute pointing to the class itself.
console.log(Person.prototype) // { constructor: f() }
console.log(Person === Person.prototype.constructor) // true
// Use the instanceof operator to check the prototype of Person on the prototype chain of instance P.
let p = new Person()
console.log(p instanceof Person) // true
Copy the code

A class is a first-class citizen of JavaScript, so it can be passed as an argument just like any other object or function reference.

let classList = [
    class {
        constructor(id) {
            this.id_ = id
            console.log(`instance The ${this.id_}`)}}]function createInstance(classDefinition, id) {
    return new classDefinition(id)
}
let foo = createInstance(classList[0].1024) // instance 1024
Copy the code

Similar to calling function expressions immediately, classes can be instantiated immediately:

// The class name is optional because it is a class expression
let p = new class Foo {
    constructor(x) {
        console.log(x)
    }
}('bar') // bar
console.log(p) // Foo {}
Copy the code

The composition of the class

Class constructor

The constructor keyword is used to create a class constructor inside the class definition block. Not defining constructor is equivalent to defining a constructor as an empty function.

class Person {} 
/ / equivalent to the
class Person {
    constructor(){}}Copy the code

The instantiation of Person using the new operator means that it should be done using the constructor function. Within constructor, you can add attributes to the newly created instance (this).

class Person {
    constructor(age, job) {
        // This example first defines a string using the object wrapper type in order to test the equality of the two objects below.
        this.name = new String("Liu")
        this.age = age
        this.sayName = () = > console.log(this.name)
        this.job = job
    }
}
// The arguments passed when the class is instantiated are used as arguments to the constructor.
let p1 = new Person(18."Front End Engineer")
console.log(p1.name, p1.age, p1.job) // Xiao Liu, 18
p1.sayName() / / liu

let p2 = new Person(18."Front End Engineer")
// Each instance corresponds to a unique member object, which is not shared on the prototype.
console.log(p1.name === p2.name) // false
Copy the code

By default, the class constructor returns this object after execution, which in this case is undefined. (Because of the strict mode inside the class, this actually refers to undefined.)

Prototype method

To share methods between instances, the Class definition syntax treats methods defined in a Class block as prototype methods.

class Person {
    constructor() {
        // Everything added to this will exist on different instances
        this.locate = () = > console.log('instance')}// Everything defined in a class block is defined in the prototype of the class
    locate() {
        console.log('prototype')}//Uncaught SyntaxError: Unexpected Identifier, cannot add primitive values or objects to the prototype as member data in the class block.
}

let p = new Person()
p.locate() // instance
Person.prototype.locate() // prototype
Copy the code

Class methods are equivalent to object properties, so they can use strings, symbols, or computed values as keys.

const symbolKey = Symbol('symbolKey')
class Person {
    stringKey() {
            console.log('invoked stringKey')
    }
    [symbolKey]() {
            console.log('invoked symbolKey')} ['computed' + 'Key'] () {console.log('invoked computedKey')}}let p = new Person()
p.stringKey() // invoked stringKey
p[symbolKey]() // invoked symbolKey
p.computedKey() // invoked computedKey
Copy the code

accessor

Class definitions also support getting and setting accessors.

class Person {
    set name(newName) {
        this.name_ = newName
    }
    get name() {
        return this.name_
    }
}

let p = new Person
p.name = "Liu"
console.log(p.name) / / liu
Copy the code

A static method

If you prefix a method with the static keyword, it means that the method is not inherited by the instance, but is called directly from the class. This is called a “static method”.

  • Only one static member can exist on each class. Multiple static members with the same name will be overwritten, and only the last one will take effect.
  • Statically methodologicalthisPoint to theClass.
  • Static method subclasses of the parent class can be inherited.
  • superMethods can call static methods on their parent classes.
class Person {
    constructor() {
        // Everything added to this will exist on different instances
        this.locate = () = > console.log('instance'.this)}// Defined on the prototype object of the class
    locate() {
        console.log('prototype'.this)}// Defined on the class itself
    static locate() {
        console.log('class'.this)}}let p = new Person
p.locate() // instance, Person {}
Person.prototype.locate() // prototype, {constructor: ƒ, locate: ƒ}
Person.locate() // class, class Person {... }, static methods are called directly through the class identifier
Copy the code

Static class methods are well suited as instance factories.

class Person {
    constructor(age) {
        this.age_ = age
    }
    sayAge() {
        console.log(this.age_)
    }
    static create() {
        Create and return a Person instance using a random age
        return new Person(Math.floor(Math.random() * 100))}}console.log(Person.create()) // Person { age_: ... }
Copy the code

Iterator and generator methods

class Person {
    // Define generator methods on prototypes
    * createNicknameIterator() {
        yield "Liu"
        yield "Note"
        yield "Xiao li"
    }
    // Define generator methods on the class
    static * createJobIterator() {
        yield "Front End Engineer"
        yield "Back-end engineer"
        yield "Full stack Engineer"}}let jobIter = Person.createJobIterator()
console.log(jobIter.next().value) // Front-end engineer
console.log(jobIter.next().value) // Backend engineer
console.log(jobIter.next().value) // Full stack engineer
let p = new Person()
let nicknameIter = p.createNicknameIterator()
console.log(nicknameIter.next().value) / / liu
console.log(nicknameIter.next().value) / / note:
console.log(nicknameIter.next().value) / / xiao li
Copy the code

Because generator methods are supported, class instances can be made iterable by adding a default iterator.

class Person {
    constructor() {
            this.nicknames = ["Liu"."Note"."Xiao li"[]} *Symbol.iterator]() {
            yield* this.nicknames.entries()
        }
}
let p = new Person()
for (let [idx, nickname] of p) {
    console.log(nickname)
}
/ / liu
/ / note:
/ / xiao li
Copy the code

You can also return only iterator instances.

class Person {
    constructor() {
            this.nicknames = ["Liu"."Note"."Xiao li"]} [Symbol.iterator]() {
            return this.nicknames.entries()
        }
}
let p = new Person()
for (let [idx, nickname] of p) {
    console.log(nickname)
}
Copy the code

Static attributes

Static properties refer to properties of the Class itself, class.propName, not properties defined on the instance object (this).

Previously only defined outside of Class, this can now be done by prefixing the static keyword.

class Person {
    sayName() {
        console.log(`${Person.greeting} The ${this.name}`)}}// Define data members on the class
Person.greeting = "My name is." // Define data members on the prototype
Person.prototype.name = "Liu"
let p = new Person()
p.sayName() // My name is Xiao Liu
//--------------------------------------------------------------------
class Person {
    sayName() {
        console.log(`${Person.greeting} The ${this.name}`)}static greeting = "My name is."
}
// Define data members on the class
Person.prototype.name = "Liu"
let p = new Person()
p.sayName() // My name is Xiao Liu

Copy the code

Class inheritance

ES6 classes support single inheritance. Using the extends keyword, you can inherit any object that has [[Construct]] and a stereotype.

class Vehicle {}
/ / a derived class
class Bus extends Vehicle {}
let b = new Bus()
console.log(b instanceof Bus) // true
console.log(b instanceof Vehicle) // true
function Person() {}
// Inherit the normal constructor
class Engineer extends Person {}
let e = new Engineer()
console.log(e instanceof Engineer) // true
console.log(e instanceof Person) // true
Copy the code

Derived classes access the class and methods defined on the stereotype through the stereotype chain. The value of this reflects the instance or class calling the corresponding method.

class Vehicle {
    identifyPrototype(id) {
        console.log(id, this)}static identifyClass(id) {
        console.log(id, this)}}class Bus extends Vehicle {
    constructor(){
        super(a)this.name = "Yutong"}}let v = new Vehicle()
let b = new Bus()
b.identifyPrototype('bus') // bus, bus {name: "yutong "}
v.identifyPrototype('vehicle') // vehicle, Vehicle {}
Bus.identifyClass('bus') // bus, class Bus extends Vehicle {... }
Vehicle.identifyClass('vehicle') // vehicle, class Vehicle {... }
Copy the code

The extends keyword can also be used in class expressions, so let Bar = class extends Foo {} is a valid syntax.

super()

Methods of derived classes can reference their archetypes through the super keyword. This keyword can only be used in derived classes, and only inside class constructors, instance methods, and static methods.

class Vehicle {
    constructor() {
        this.hasEngine = true
    }
    static identify() {
        console.log('vehicle')}}class Bus extends Vehicle {
    constructor() {
        /** * Do not refer to this before calling super(), otherwise a ReferenceError will be raised. This is because the subclass's own this object must be molded by the parent class's constructor to get the same instance attributes and methods as the parent class, and then processed with the subclass's own instance attributes and methods. If you don't call super, your subclasses don't get this. * /
        super(a)// Equivalent to super.constructor(), using super in the class constructor calls the superclass constructor.
        console.log(this instanceof Vehicle) 
        console.log(this)}static identify() {
        super.identify() Static methods defined on the parent class can be called by super in static methods.}}new Bus()
// true
// Bus { hasEngine: true }
Bus.identify() 
//vehicle
Copy the code

Note: ES6 adds an internal property [[HomeObject]] to class constructors and static methods, which is a pointer to the object that defines the method. This pointer is automatically assigned and can only be accessed within the JavaScript engine. Super is always defined as a prototype for [[HomeObject]].

There are a few things to note when using super.

Super can only be used in derived class constructors and static methods.

//
class Vehicle {
    constructor() {
        super(a)// Uncaught SyntaxError: 'super' keyword unexpected here}}Copy the code

You cannot refer to the super keyword alone, either by calling the constructor or by referring to static methods.

class Vehicle {}
class Bus extends Vehicle {
    constructor() {
        console.log(super)// Uncaught SyntaxError: 'super' keyword unexpected here}}Copy the code

Calling super() calls the superclass constructor and assigns the returned instance to this.

class Vehicle {}
class Bus extends Vehicle {
    constructor() {
        super(a)console.log(this instanceof Vehicle)
    }
}
new Bus() // true
Copy the code

Super () behaves like a constructor call; if you need to pass arguments to the parent constructor, you need to pass them manually.

class Vehicle {
    constructor(licensePlate) {
        this.licensePlate = licensePlate
    }
}
class Bus extends Vehicle {
    constructor(licensePlate) {
        super(licensePlate)
    }
}
console.log(new Bus('CODE999')) // Bus {licensePlate: "CODE999"}
Copy the code

If the class constructor is not defined, super() is called when the derived class is instantiated and all arguments passed to the derived class are passed.

class Vehicle {
    constructor(licensePlate) {
        this.licensePlate = licensePlate
    }
}
class Bus extends Vehicle {}
console.log(new Bus('CODE999')) // Bus {licensePlate: "CODE999"}
Copy the code

In class constructors, you cannot refer to this before calling super().

class Vehicle {}
class Bus extends Vehicle {
    constructor() {
        console.log(this)}}new Bus()
// ReferenceError: Must call super constructor in derived class
// before accessing 'this' or returning from derived constructor
Copy the code

If you explicitly define a constructor in a derived class, you must either call super() in it or return an object in it.

class Vehicle {}
class Car extends Vehicle {}
class Bus extends Vehicle {
    constructor() {
        super()}}class Van extends Vehicle {
    constructor() {
        return{}}}console.log(new Car()) // Car {}
console.log(new Bus()) // Bus {}
console.log(new Van()) / / {}
Copy the code

Abstract base class

New.target holds classes or functions called through the new keyword. You can prevent instantiation of an abstract base class by detecting whether new.target is an abstract base class at instantiation time.

// Abstract base class
class Vehicle {
    constructor() {
        console.log(new.target)
        if (new.target === Vehicle) {
            throw new Error('Vehicle cannot be directly instantiated')}}}/ / a derived class
class Bus extends Vehicle {}
new Bus() // class Bus {... }, new.target points to the class definition of the initializing class.
new Vehicle() // class Vehicle {... }, new.target points to the class definition of the initializing class.
// Error: Vehicle cannot be directly instantiated
Copy the code

By checking in the abstract base class constructor, you can require that a derived class must define a method. Because the prototype method exists before the class constructor is called, you can check the corresponding method with the this keyword.

// Abstract base class
class Vehicle {
    constructor() {
        if (new.target === Vehicle) {
            throw new Error('Vehicle cannot be directly instantiated')}if (!this.foo) {
            throw new Error('Inheriting class must define foo()')}console.log('success! ')}}/ / a derived class
class Bus extends Vehicle {
    foo(){}}/ / a derived class
class Van extends Vehicle {}
new Bus() // success!
new Van() // Error: Inheriting class must define foo()
Copy the code

Inheriting built-in types

The ES6 class provides a smooth mechanism for inheriting built-in reference types, which developers can easily extend.

class SuperArray extends Array {
    shuffle() {
        // Shuffle algorithm
        for (let i = this.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1))this[i], this[j]] = [this[j], this[i]]
        }
    }
}
let a = new SuperArray(1.2.3.4.5)
console.log(a instanceof Array) // true
console.log(a instanceof SuperArray) // true
console.log(a) // [1, 2, 3, 4, 5]
a.shuffle()
console.log(a) // [3, 1, 4, 5, 2]
Copy the code

Some methods of built-in types return new instances. By default, the type of the returned instance is the same as that of the original instance.

class SuperArray extends Array {}
let a1 = new SuperArray(1.2.3.4.5)
let a2 = a1.filter(x= >!!!!! (x %2)) / /!!!!! Is to convert all other types to Boolean
console.log(a1) // [1, 2, 3, 4, 5]
console.log(a2) / / [1, 3, 5]
console.log(a1 instanceof SuperArray) // true
console.log(a2 instanceof SuperArray) // true
Copy the code

If you want to override this default behavior, you can override the symbol.species accessor, which determines the class to use when creating the returned instance.

class SuperArray extends Array {
    static get[Symbol.species]() {
        return Array}}let a1 = new SuperArray(1.2.3.4.5)
let a2 = a1.filter(x= >!!!!! (x %2))
console.log(a1) // [1, 2, 3, 4, 5]
console.log(a2) / / [1, 3, 5]
console.log(a1 instanceof SuperArray) // true
console.log(a2 instanceof SuperArray) // false
Copy the code

Class with

It is a common JavaScript pattern to group different classes of behavior into a single class. While ES6 does not explicitly support multi-class inheritance, this behavior can be easily modeled with existing features.

Note that the object.assign () method is designed to blend in Object behavior. It is only necessary to implement a mixin expression yourself if you need to mixin the behavior of the class. If you just want to mix in multiple Object attributes, use object.assign ().

The extends keyword can be followed by an expression (which can be resolved to a class or a constructor). This expression is evaluated when the class definition is evaluated.

class Vehicle {}

function getParentClass() {
    console.log('evaluated expression')
    return Vehicle
}
class Bus extends getParentClass(a){}// evaluated expression
Copy the code

The mixin pattern can be implemented by concatenating multiple mixin elements in an expression that eventually resolves to a class that can be inherited.

If the Person class needs to combine A, B, and C, then you need some mechanism to combine A, B, and C into the superclass by having B inherit from A, C inherit from B, and Person inherit from C. There are different strategies for implementing this pattern. One strategy is to define a set of “nested” functions, each of which takes a superclass as an argument, and to define the mixin class as a subclass of that argument and return that class. These composite functions can be called sequentially to form superclass expressions:

class Vehicle {}
let FooMixin = (Superclass) = > class extends Superclass {
    foo() {
        console.log('foo')}}let BarMixin = (Superclass) = > class extends Superclass {
    bar() {
        console.log('bar')}}let BazMixin = (Superclass) = > class extends Superclass {
    baz() {
        console.log('baz')}}class Bus extends FooMixin(BarMixin(BazMixin(Vehicle))) {}
let b = new Bus()
b.foo() // foo
b.bar() // bar
b.baz() // baz
Copy the code

We can expand the nested call by writing a helper function:

class Vehicle {}
let FooMixin = (Superclass) = > class extends Superclass {
    foo() {
        console.log('foo')}}let BarMixin = (Superclass) = > class extends Superclass {
    bar() {
        console.log('bar')}}let BazMixin = (Superclass) = > class extends Superclass {
    baz() {
        console.log('baz')}}function mix(BaseClass, ... Mixins) {
    return Mixins.reduce((accumulator, current) = > current(accumulator), BaseClass)
}
class Bus extends mix(Vehicle.FooMixin.BarMixin.BazMixin) {}
let b = new Bus()
b.foo() // foo
b.bar() // bar
b.baz() // baz
Copy the code

Class implementation Principle

The underlying implementation of Class can be seen with Babel.

The realization of the class

Class constructor

/ / before the conversion
class Person {
  constructor(name) {
        this.name = name
        this.sayName = () = > {
            console.log("My name is." + this.name)
        }
    }
}
/ / after the transformation
function _classCallCheck(instance, Constructor) {
    if(! (instanceinstanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function"); }}var Person = function Person(name) {
    "use strict";

    var _this = this;

    _classCallCheck(this, Person);

    this.name = name;

    this.sayName = function () {
        console.log("My name is." + _this.name);
    };
};
Copy the code
  1. Start by declaring a _classCallCheck method that checks to see if the class is invoked using the new keyword. If not (that is, calling Person() directly), the Cannot Call a class as a function error is thrown.

  2. The class is use strict.

  3. Assign both constructor properties and methods to this (instance).

  4. Execute the logic inside constructor.

  5. Return this (by default, the class constructor returns this after execution).

The prototype method and the static method

/ / before the conversion
class Person {
    // Prototype method
    sayAge() {
        console.log("My year" + this.age)
    }
    // Static attributes
    static job = "Front End Engineer"
    // Static method
    static sayJob() {
        console.log("My job is" + Person.job)
    }
}
// Prototype attributes
Person.prototype.age = 18

/ / after the transformation
function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        // Enumerable defaults to false
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor); }}function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, {
            value: value,
            enumerable: true.configurable: true.writable: true
        });
    } else {
        obj[key] = value;
    }
    return obj;
}

var Person = /*#__PURE__*/ function () {
    "use strict";

    function Person() {
        _classCallCheck(this, Person);
    }

    _createClass(Person, [{
        key: "sayAge".value: function sayAge() {
            console.log("My year" + this.age); }}], [{key: "sayJob".value: function sayJob() {
            console.log("My job is"+ Person.job); }}]);returnPerson; } (); _defineProperty(Person,"job"."Front End Engineer");

Person.prototype.age = 18;
Copy the code
  1. As you can see, the Person becomes an IIFE, which still returns the Person class after execution, but encapsulates a constructor and a _createclass function inside.

  2. The Person constructor, which contains the _classCallCheck method that we are checking to see if we are calling with new. Although constructor is not shown above, Class automatically creates one by default, which is what the book calls empty constructor.

  3. The _defineProperties function, which takes a class (or a class stereotype) and an array of objects, iterates through each object in the array, defines the properties of each method, and adds them to the class (or a class stereotype) one by one. The features involved here include:

    • enumberable: Whether the property (method) is enumerable. Use this property if the method itself has defined it; If not, define the method as not enumerable.
    • configurable: Whether the property (method) can be configured.
    • writableIf the property is a data property rather than an accessor property, there will be onevalueTo set the property to writable.

    The default enumerable is false, and the control system is true, which disables any method like object.keys ().

    And then you can determine if it’s a getter and setter by checking if the value exists.

    Add value and writable properties to descriptor if value is present, or use get and set properties if not.

  4. The _createClass function. First, it can take three arguments:

    • The first argument: class (here isPersonClass);
    • The second argument: an array of objects, each of which is a property description object of the prototypical method of the class (here)sayAge());
    • Third argument: an array of objects, each of which is a property description object for the class’s static methodssayJob()).

    It then checks to see if a second and third argument are passed in, and if so, calls the _defineProperties function to define the stereotype method for the class’s stereotype and the static method for the class itself.

  5. The _defineProperty function, which takes the class, attribute name, and attribute value as parameters and, if the specified attribute is in a class or class stereotype chain, defines the attributes for each attribute and adds them to the class one by one; Or, override the value of an attribute on a class. The features involved here include:

    • enumberable: This method is enumerable.
    • configurable: This method is configurable.
    • writable: This method can be overridden.

    Finally, return the class.

inheritance

/ / before the conversion
class Vehicle {}class Bus extends Vehicle {
    constructor() {
        super()}}/ / after the transformation
require("core-js/modules/es.symbol.description.js");

function _typeof(obj) {
    "@babel/helpers - typeof";
    if (typeof Symbol= = ="function" && typeof Symbol.iterator === "symbol") {
        _typeof = function _typeof(obj) {
            return typeof obj;
        };
    } else {
        _typeof = function _typeof(obj) {
            return obj && typeof Symbol= = ="function" && obj.constructor === Symbol&& obj ! = =Symbol.prototype ? "symbol" : typeof obj;
        };
    }
    return _typeof(obj);
}

function _inherits(subClass, superClass) {
    if (typeofsuperClass ! = ="function"&& superClass ! = =null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true.configurable: true}});if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}

function _createSuper(Derived) {
    var hasNativeReflectConstruct = _isNativeReflectConstruct();
    return function _createSuperInternal() {
        var Super = _getPrototypeOf(Derived),
            result;
        if (hasNativeReflectConstruct) {
            var NewTarget = _getPrototypeOf(this).constructor;
            result = Reflect.construct(Super, arguments, NewTarget);
        } else {
            result = Super.apply(this.arguments);
        }
        return _possibleConstructorReturn(this, result);
    };
}

function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}

function _isNativeReflectConstruct() {
    if (typeof Reflect= = ="undefined" || !Reflect.construct) return false;
    if (Reflect.construct.sham) return false;
    if (typeof Proxy= = ="function") return true;
    try {
        Boolean.prototype.valueOf.call(Reflect.construct(Boolean[],function () {}));
        return true;
    } catch (e) {
        return false; }}function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
}
/ / to omit
function _classCallCheck(instance, Constructor) {
    if(! (instanceinstanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function"); }}var Vehicle = function Vehicle() {
    "use strict";

    _classCallCheck(this, Vehicle);
};

var Bus = /*#__PURE__*/ function (_Vehicle) {
    "use strict";

    _inherits(Bus, _Vehicle);

    var _super = _createSuper(Bus);

    function Bus() {
        _classCallCheck(this, Bus);

        return _super.call(this);
    }

    return Bus;
}(Vehicle);

Copy the code
  • _typeof(obj)Function is a utility function, mainly for the purpose ofSymbolDo the right thing.
    • It first checks whether the current environment supports nativeSymbolIf it is supported, return it directlytypeof objCalculation result of expression;
    • If not, check againobjIs this done through polyfillSymbolIf so, return its type (that is, return"symbol"), if not, returntypeof objThe calculation results of.
    • In this case, this function assumes that our current environment is native supportedSymbolOr supported via Polyfill.
  • _setPrototypeOf(o, p)Function checks whether the current environment supports the callObject.setPrototypeOf()Method, if not supported, passes__proto__Manually create prototype relationships for instances.
  • _getPrototypeOf(o)Function checks whether the current environment supports the callObject.setPrototypeOf()Method, if supportedObject.getPrototypeOf()Methods; If not, declare oneObject.setPrototypeOf()Function, returnso.__proto__.
  • _possibleConstructorReturn(self,call)This helper function is mainly used for implementationsuper()The effect of, corresponds toParasitic combinatorial inheritanceOn isBorrow constructor inheritanceThe difference is that the method returns onethisAnd assign to the subclassthis.
    • Two parametersselfRepresents an instance of the constructor,callRepresents the return value of the constructor;
    • ifA judgment inside a block is a checkcallWhen it is an object or function, it is returned directly.
    • Otherwise return_assertThisInitialized(self).
  • _assertThisInitialized(self)When we call this method, what we expect isthisIt’s already initialized.
    • If the tests show thatthisisundefined, will throw an error telling us that there is no callsuper()So you can’t get itthis.
    • Otherwise returnthis.
    • Why usevoid 0withoutundefinedbecauseundefinedIt can be rewritten in earlier versions of IE.
  • _isNativeReflectConstruct()This method is used to check whether the current environment supports nativeReflect.
    • useReflect.constructThe main reason is when the call comesReflect.construct()Create an object,new.targetThe value is automatically specified totarget(ornewTargetAs long asnewTargetSpecified).
    • Solved the use ofSuper.applyorObject.createWill be inside the constructornew.targetPoint to theundefinedThe problem.

Take a look at the code at the end:

var Bus = /*#__PURE__*/ function (_Vehicle) {
    "use strict";

    _inherits(Bus, _Vehicle);

    var _super = _createSuper(Bus);

    function Bus() {
        _classCallCheck(this, Bus);

        return _super.call(this);
    }

    return Bus;
}(Vehicle);
Copy the code

Bus here is also an IIFE, and actually returns a Bus subclass structure function, except that it also encapsulates calls to other methods.

  • The _inherits(Bus, _Vehicle) function is one of the core methods to implement inheritance. It can be said that its essence is the parasitic combinational inheritance in ES5.

    • This method takes a parent and a child class as arguments
      1. checkextendsThe inheritance target (the parent class) of thenull;
      2. useObject.create, set the subclassprototypeProperties of the__proto__Property pointing to the parent classprototypeProperties;
      3. Set the subclass’s__proto__Attribute points to the parent class (the purpose of which is for subclasses to inherit the static methods of the parent class).
  • The _super.call(this) function, which is _createSuper(Derived), creates a super() that calls the parent element constructor, primarily dealing with its return type.

    • Get the prototype, which is _Vehicle (inheritance was done earlier).

    • Check whether the current environment supports Reflect.

      • Support, implementReflect.construct(Super, arguments, NewTarget), and finally returns a baseSuperAn instance of the parent constructor is executednew Super(... arguments).
        • Of this instance__proto__theconstructorisNewTargetSo in a way, you could say this is an instance of a subclass, but it has all the attributes of its parent class.
      • If no, go toSuper.apply(borrowing constructor inheritance).
    • Check the return value

      • There are three possible results of result:

        • An instance of a subclass that inherits all attributes of the parent class instance;
        • It may be a non-empty object returned by a custom constructor in the parent class.
        • Probably returned by defaultundefined.
      • If the first and second cases, return result directly.

      • In the third case, assert the subclass instance that was passed in (enhanced by super.apply), namely this, and return it.

Reference:

Javascript Advanced Programming fourth edition

Class MDN

Introduction to ECMAScript 6 (ES6) standards

JS class summary

Discuss the principle of ES6 inheritance from Babel translation process

How does ES6 series Babel compile classes?

How does ES6 series Babel compile classes?

Start with Prototype — Class and extends in ES6

Es6 classes and inheritance implementation principles