Inheritance of ES5/ES6

Keywords: Javascript Attribute Programming

Prototype chain

  • Constructor/prototype/instance relationship
  • Each constructor has a prototype, which contains a pointer to the constructor, and an instance contains a pointer to the interior of the prototype object.
  • If you try to use an attribute or method of an object (instance), you will first look for the attribute inside the object, if you can't find it in the instance.prototype of the object, and if you can't find it, you will continue to look up the chain of _proto_ until you find Object.prototype.
  • Everything in javascript is an object, so you can start from this chain.
  • JavaScript inheritance is different from traditional object-oriented inheritance by classes, but by prototype chains.

ES5 Inheritance

  • Copy inheritance (inheritance through deep copy)
  • Prototype inheritance

    • Disadvantage: Only prototype methods can be inherited
  • Borrowing constructor inheritance

    • Disadvantage: Only instance attributes can be inherited
  • Combinatorial Inheritance

    • Disadvantage: In any case, the constructor is called twice (when the parent instance is created, when the parent constructor is not called within the child constructor)
  • Composite parasitic inheritance (perfect inheritance, but unable to inherit static methods, static attributes of parent classes)
function Parent() {}

function Child() {
    // Inheritance of parent instance attributes
    Parent.call(this) // If the parent class has parameters written after this
}

// Inheritance of the parent prototype method
Child.prototype = Object.create(Parent.prototype)

// Modifying constructor pointing of subclass prototypes
Child.prototype.constructor = Child 
Two points of attention:
  1. Object.create(proto, [propertiesObject]) MDN

    Create a new object, using existing objects to provide the _proto of the newly created object__
    
    Object.create(null) // Create an empty object without a prototype
     The second parameter adds attribute descriptors
    
    js advanced programming replaces this method with a bit of code
    function createObject(P) {
        var F = function() {}
        F.prototype = P.prototype
        return new F()
    }
  2. Why modify the constructor pointing of the subclass prototype? Ruan Yifeng

    To sum up briefly:
    Any prototype object has a constructor attribute that points to its constructor
     More importantly, each instance also has a constructor attribute, which by default calls prototype's constructor attribute.
    
    If there is no amended line of code, the results are as follows
    var c = new C()
    c.constructor === Child // false
    Child.prototype.constructor === Child // false
    
    c.constructor === Parent // true
    Child.prototype.constructor === Parent // true
    
    This obviously leads to chaos in the inheritance chain (c is explicitly generated with the constructor Child), so we have to correct it manually.

ES6 Inheritance

Essentially, the inheritance of ES6 is realized by prototype chain

  • class extends keyword
class Parent {
    static sayAge() {
        return '18'
    }
      constructor(name) {
        this.name = 'name'
    }
}

class Child extends Parent {
    constructor(name, age) {
        /**
         * If you write a constructor, you must call the super method, because the child class's own this object must first be shaped through the parent constructor
         * If you don't write super, you won't get this object. When you write new, you will report an error.
         * Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
         * 
         * ES5 Essentially, create the instance object this of the subclass first, and then add the method of the parent class to this (Parent.call(this))
         * ES6 In essence, the attributes and methods of the parent instance object are first added to this (super method must be called first), and then the subclass constructor is used to modify this.
        */
        super(name, age)
          this.age = age
    }
}
// Note: Inheritance of es6 can inherit static methods and attributes of parent classes, while inheritance of ES5 cannot.

ES6 inheritance code after Babel transcoding

// For easy viewing, the code is beautified and omitted.
"use strict";

// Check whether the subclass invokes the super method
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;
}

// Get the object that the prototype chain of the subclass points to, that is, the parent class
function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf
    : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
      };
  return _getPrototypeOf(o);
}

// Core Inheritance Method
function _inherits(subClass, superClass) {
  // ...
  // The paragraph inherited from es5
  subClass.prototype = Object.create(superClass.prototype, {
    constructor: { 
        value: subClass, // Modified constructor pointing
        writable: true, 
        configurable: true 
    }
  });
  // The inheritance principle for implementing static attributes and methods is: Child. _proto_ = Parent
  // That is, prototype = Parent on the previous subclass (which now corresponds to an instance), which can use static attributes and methods of the parent class.
  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);
}

// The creation and detection of omitted classes

var Parent =
  /*#__PURE__*/
  (function() {
    _createClass(Parent, null, [
      {
        key: "sayAge",
        value: function sayAge() {
          return "18";
        }
      }
    ]);

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

      this.name = "name";
    }

    return Parent;
  })();

var Child =
  /*#__PURE__*/
  (function(_Parent) {
    _inherits(Child, _Parent);

    function Child(name, age) {
      var _this;

      _classCallCheck(this, Child);
      
      // The next line of code is the effect of calling super. If you don't call super subclasses, you won't have this
      _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name, age));

      /***
        * If super in the comment source code is compiled into the following code
        * _this.age = age;
        * return _possibleConstructorReturn(_this);
        * Because the super subclass is not called and this is not yet there, the next line reports an error directly.
        *
        * If _this.age = age is not written in the source code
        * You go directly to the _assertThisInitialized method and then report an error without calling the super method
      */

      _this.age = age;
      return _this;
    }

    return Child;
  })(Parent);

var c = new Child();
// If you don't use babel transcoding, run it directly in the browser and don't write super, the results are as follows
// Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  • Inheritance of native constructors cannot be achieved through extends ES6 Ruan Yifeng
  • Because subclasses can't get objects inside the native parent class, even through call.

new operator

Inheritance is mentioned above. What's the principle of the operator new that generates instances?

var obj = {}
obj.__proto__ = Child.prototype
F.call(obj)

// 1. Create an empty object
// 2. Pointing the _proto_ attribute of the empty object to the prototype attribute of the constructor ==> inheritance prototype attribute method
// 3. Replace this pointer of the constructor with obj (instance), and then call the constructor ==> inheritance instance attribute method

Several methods related to prototype chains

  1. hasOwnProperty: This method only finds whether the object itself has an attribute, not on the prototype chain.
  2. A. isProperty Of (instanceA): Determine whether A is the prototype object of instanceA
  3. instanceof: An instance of determining whether an object is a constructor

Posted by dexhaven on Tue, 23 Jul 2019 00:12:38 -0700