Article From:https://www.cnblogs.com/yunqishequ/p/9967827.html

Preface

ES5 Parasitic combinatorial inheritance

function Parent (name) {
    this.name = name;
}

Parent.prototype.getName = function () {
    console.log(this.name)
}

function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}

Child.prototype = Object.create(Parent.prototype);

var child1 = new Child('kevin', '18');

console.log(child1);

The schematic diagram of the prototype chain is as follows:

 Parasitic Composite Inheritance Prototype Chain Diagram

We introduced parasitic combinatorial inheritance in JavaScript Deep Inheritance in Multiple Ways and Advantages and Disadvantages.

To quote the praise of Parasitic Combinatorial Inheritance in JavaScript Advanced Programming is:

The efficiency of this approach is that it only calls the Parent constructor once, and thus avoids creating unnecessary and redundant attributes on Parent. prototype. At the same time, the prototype chain remains unchanged; therefore, ins can be used normally.Tanceof and isPrototype Of. Developers generally believe that parasitic combinatorial inheritance is the best inheritance paradigm for reference types.

ES6 extend

Class It is much clearer and more convenient to implement inheritance through extends keyword than by modifying prototype chain in ES5.

The code of ES5 above corresponds to ES6.

class Parent {
    constructor(name) {
        this.name = name;
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name); // Call the constructor (name) of the parent classthis.age = age;
    }
}

var child1 = new Child('kevin', '18');

console.log(child1);

It is noteworthy that:

super The keyword represents the constructor of the parent class, which is equivalent to the Parent. call (this) of ES5.

The subclass must call the super method in the constructor method, otherwise an error will be reported when a new instance is created. This is because the subclass does not have its own this object, but inherits the parent’s this object and processes it. If super is not calledMethod, subclasses will not get this object.

It is also for this reason that in the constructor of the subclass, only after calling super, can we use this keyword, otherwise it will cause an error.

Subclass u proto_u

In ES6, static methods of parent classes can be inherited by subclasses. For instance:

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod(); // 'hello'

This is because Class, as a constructor of grammatical sugar, has both prototype and u proto_ attributes, so there are two inheritance chains at the same time.

(1)The u proto_ attribute of the subclass, which represents the inheritance of the constructor, always points to the parent class.

(2)The u proto_ attribute of the prototype attribute of the subclass, which represents the inheritance of the method, always points to the prototype attribute of the parent class.

class Parent {
}

class Child extends Parent {
}

console.log(Child.__proto__ === Parent); // true
console.log(Child.prototype.__proto__ === Parent.prototype); // true

ES6 The schematic diagram of the prototype chain is as follows:

 ES6 class prototype chain schematic diagram

We will find that ES6 has one more class than parasitic combinatorial inheritance.Object.setPrototypeOf(Child, Parent)Steps.

Inheritance goal

extends Keyword can be followed by multiple types of values.

class B extends A {
}

A of the above code, as long as it is a function with prototype attribute, can be inherited by B. Because functions have prototype properties (except Function. prototype functions), A can be any function.

In addition to functions, the value of A can also be null, when uuuuuuuuuuuextend null When:

class A extends null {
}

console.log(A.__proto__ === Function.prototype); // true
console.log(A.prototype.__proto__ === undefined); // true

Babel Compile

The ES6 code:

class Parent {
    constructor(name) {
        this.name = name;
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name); // Call the constructor (name) of the parent classthis.age = age;
    }
}

var child1 = new Child('kevin', '18');

console.log(child1);

Babel How is it compiled? We can try it out on Babel’s official website:

'use strict';

function _possibleConstructorReturn(self, call) {
    if (!self) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

var Parent = function Parent(name) {
    _classCallCheck(this, Parent);

    this.name = name;
};

var Child = function(_Parent) {
    _inherits(Child, _Parent);

    function Child(name, age) {
        _classCallCheck(this, Child);

        // Call the constructor (name) of the parent classvar _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name));

        _this.age = age;
        return _this;
    }

    return Child;
}(Parent);

var child1 = new Child('kevin', '18');

console.log(child1);

We can see that Babel created the _inherits function to help achieve inheritance, and the _possibleConstructorReturn function to help determine the return value of the call to the parent constructor. Let’s take a closer look at the code.

_inherits

function _inherits(subClass, superClass) {
    // extend The inheritance goal must be a function or nullif (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }

    // Parasitic composite inheritance, similar to ES5, uses Object. create to set the u proto_ attribute of the prototype attribute of the subclass to point to the prototype attribute of the parent class.SubClass.proTotype =Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });

    // Set the u proto_ attribute of the child class to point to the parent classif (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

As for Object. create (), we usually pass in a parameter when we use it. In fact, we support two parameters. The second parameter indicates the attributes to be added to the newly created object. Note that this is to add attributes to the newly created object, that is, the return value, rather than the original of the newly created object.Add on type object.

For instance:

// Create an object that takes another empty object as a prototype and has an attribute pconst o = Object.create({}, { p: { value: 42 } });
console.log(o); // {p: 42}
console.log(o.p); // 42

To be more complete:

const o = Object.create({}, {
    p: {
        value: 42,
        enumerable: false,
        // This property is not writableWritable:false,
        configurable: true
    }
});
o.p = 24;
console.log(o.p); // 42

So for this code:

subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });

The purpose is to add a configurable, writable and non-enumerable constructor attribute to subClass. prototype, which is subClass.

_possibleConstructorReturn

The function is called as follows:

var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name));

We simplify it as follows:

var _this = _possibleConstructorReturn(this, Parent.call(this, name));

_possibleConstructorReturn The source code is:

function _possibleConstructorReturn(self, call) {
    if (!self) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

Here we judge.Parent.call(this, name) The type of the return value, huh? There are many other types of this value?

For such a class:

class Parent {
    constructor() {
        this.xxx = xxx;
    }
}

Parent.call(this, name) The value must be undefined. But what if we return it in the constructor function? For example:

class Parent {
    constructor() {
        return {
            name: 'kevin'
        }
    }
}

We can return various types of values, even null:

class Parent {
    constructor() {
        return null
    }
}

Let’s move on to this judgment:

call && (typeof call === "object" || typeof call === "function") ? call : self;

Note that the meaning of this sentence is not to judge whether the call exists or not. If it exists, it is executed.(typeof call === "object" || typeof call === "function") ? call : self

Because&& Operator priority is higher than? :,So the meaning of this sentence should be:

(call && (typeof call === "object" || typeof call === "function")) ? call : self;

For the value of Parent. call (this), if it is of object type or function type, it returns Parent. call (this), if it is of null or basic type, or unde.Fined, returns self, which is the subclass of this.

That’s why this function is named uuuuuuuuuuuu_possibleConstructorReturn

summary

var Child = function(_Parent) {
    _inherits(Child, _Parent);

    function Child(name, age) {
        _classCallCheck(this, Child);

        // Call the constructor (name) of the parent classvar _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name));

        _this.age = age;
        return _this;
    }

    return Child;
}(Parent);

Finally, we look at how to achieve inheritance as a whole:

First of all, to implement the policy of uuuuuuuuuuu_inherits(Child, Parent),Establish the prototype chain relationship between Child and arent, i.e.Object.setPrototypeOf(Child.prototype, Parent.prototype) SumObject.setPrototypeOf(Child, Parent)

Then callParent.call(this, name),The initial value of this subclass constructor is determined according to the type of return value of the Parent constructor.

Finally, the value of _this is modified according to the subclass constructor, and then returned.

ES6 series

ES6 The series is expected to write about 20 articles, aiming at deepening the understanding of ES6 knowledge points, focusing on block-level scopes, label templates, arrow functions, Symbol, Set, Map and Promise simulation implementation, module loading scheme, asynchronous processing and so on.

If there are any mistakes or inaccuracies, please be sure to correct them. Thank you very much.

 

Original link
This article is the original content of Yunqi Community, which can not be reproduced without permission.

Leave a Reply

Your email address will not be published. Required fields are marked *