The prototype

JavaScript is object-based, and functions are also objects. Every function/class in JavaScript has a Prototype property that defines the common ancestor of the object produced by the constructor. Properties and methods defined on it can be shared by object instances.

By default, all stereotype objects automatically get a property called constructor that refers back to the constructor associated with them.

function Test {}console.log(Test.prototype === Test); // true
Copy the code

Each object has an internal attribute [[Prototype]], also known as an implicit Prototype. This property points to the stereotype of the constructor. There is no standard way to access [[Prototype]] in JS, but Firefox, Safari, and Chrome will expose __proto__ properties on every object.

By default, objects created by Object literals implicitly point to Object.prototype, the equivalent of new Object().

In the Chrome console, an object’s implicit prototype constructor shows what type of function/class the object is of.

Prototype chain

When a property is accessed through an object, a search is initiated by the name of the property. The search begins with the object instance itself. If a given name is found on this instance, the value of that name is returned. If the property is not found, the search follows the pointer into the prototype object, and when the property is found on the prototype object, the corresponding value is returned.

Object inheritance is implemented through the [[Prototype]] property to form a Prototype chain.

  • For JavaScriptMost of theObject, which eventually inherits fromObject.prototype
  • Object.prototype.__proto__ == nullAnd,Cannot be changed
  • All functions/classes inherit fromFunction.prototypeSo all of the functions areFunctiontype

JavaScript default inheritance:

ES5 implements inheritance

Embezzled constructors

The parent constructor is called in the subclass constructor, and the apply and Call methods are used to execute the constructor in the context of the newly created object.

function SuperType() {
    this.colors = 'red';
}
function SubType() {
    SuperType.call(this); / / inherit the SuperType
}
Copy the code

Advantages: One advantage of the stolen constructor is that you can pass arguments to the superclass constructor in the subclass constructor

Disadvantages: Because there is no stereotype chain, methods must be defined in constructors to inherit methods, and subclasses cannot be shared, making function reuse impossible

Combination of inheritance

Combinatorial inheritance combines the prototype chain with the embeded constructor to realize the inheritance of attributes and the inheritance of methods through the prototype chain.

function SuperType(name){
    this.name = name;
    this.colors = ["red"."blue"."green"];
}
SuperType.prototype.sayName = function(){
    console.log(this.name); 
}

function SubType(name, age){
    SuperType.call(this, name); // Inherit attributes
    this.age = age;
}

SubType.prototype = new SuperType(); // Inheritance method
Copy the code

Advantages: Compensates for the use of stolen constructors and retains the ability of the instanceof operator and isPrototypeOf() method to recognize synthesized objects

Disadvantages: There are efficiency issues, the main one being that the superclass constructor is always called twice

Primary inheritance

In 2006, Douglas Crockford wrote an article called Prototypal Inheritance in JavaScript. This article introduces an inheritance approach that does not involve constructors strictly. His starting point is that information can be shared between objects through prototypes without custom types. Finally, the paper gives a function:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
} 
Copy the code

Primitive inheritance applies when you have an object and want to create a new object based on it. You need to pass this object to object() first, and then modify the returned object appropriately.

The new object.create () method in ES5 implements this intent, taking two parameters: the Object as an implicit prototype for the new Object, and the additional properties of the new Object

Parasitic inheritance

Parasitic inheritance is similar to formal inheritance in that you create a function that simply encapsulates the inheritance process, enhances the object internally in some way, and finally returns the object as if it had really done all the work.

function createAnother(original){
    var clone = object(original); Create a new object by calling the function
    clone.sayHi = function(){ // Enhance the object in some way
        console.log("hi");
    };
    return clone; // Return the object
} 
Copy the code

The object() function is not required for parasitic inheritance, and any function that returns a new object can be used here.

Parasitic combinatorial inheritance

Parasitic combinatorial inheritance, that is, inheriting properties by borrowing constructors and inheriting methods through a hybrid form of stereotype chains. The basic idea behind this is that instead of calling the constructor of the supertype to specify the stereotype of the subtype, all you need is a copy of the stereotype of the supertype.

function inherit(subType, superType){
    var prototype = object(superType.prototype); // Create an object
    prototype.constructor = subType; // Enhance objects
    subType.prototype = prototype; // Specify the object
}
Copy the code

The efficiency of parasitic combinatorial inheritance is that it calls the SuperType constructor only once and thus avoids creating unnecessary, redundant properties on subtype. prototype. The advantages of combinatorial inheritance are maintained and the disadvantages of combinatorial inheritance are eliminated.

It is also convenient to use:

function SuperType(name){
    this.name = name;
    this.colors = ["red"."blue"."green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
}
function SubType(name, age){
    SuperType.call(this, name); // Implement attribute inheritance by borrowing constructors
    this.age = age;
}
inherit(SubType, SuperType); // Implement method inheritance through the prototype chain
Copy the code

Parasitic combinational inheritance is also known as the Holy Grail pattern, a favorite implementation:

var inherit = (function () {
    var F = function () {};return function (target, origin) {
        F.prototype = origin.prototype;
        target.prototype = new F();
        target.prototype.constructor = target;
        target.prototype.uber = origin.prototype; // Make it easy to find the final inherited prototype}; } ());Copy the code

ES6 implements inheritance

Object literal inheritance

Object. GetPrototyeOf was added in ES5 to get the implicit prototype of an Object, but there is no standard way to change the implicit prototype of an instantiated Object.

ES6 has added Object.setPrototypeOf to change the implicit prototype of an Object, using which inheritance can be implemented.

const person = {
    greet() {
        console.log('hi'); }}const man = {
    name: 'man'
}

Object.setPrototypeOf(man, person);
console.log(man);
Copy the code

ES6 references the super keyword, which makes it easier to access object prototypes. You can use super when overriding inherited methods but using the original method.

Differences between ES5 and ES6 practices:

const person = {
    getGreeting() {
        return 'hi! '; }}const friend1 = {
    getGreeting() {
        return Object.getPrototypeOf(this).getGreeting.call(this) + 'my friend'; }}const friend2 = {
    getGreeting() {
        return super.getGreeting() + 'my friend'; }}Object.setPrototypeOf(friend1, person);

console.log(friend1.getGreeting());  // hi! my friend

Object.setPrototypeOf(friend2, person);

console.log(friend2.getGreeting());  // hi! my friend
Copy the code

Super.getgreeting () equals object.getPrototypeof (this).getgreeting. Call (this)

Problems with ES5: When there is multiple inheritance, problems occur due to this.

const person = {
    getGreeting() {
        return 'hi! '; }}const friend = {
    getGreeting() {
        return Object.getPrototypeOf(this).getGreeting.call(this) + 'my friend'; }}Object.setPrototypeOf(friend, person);

const obj = Object.create(friend);

obj.getGreeting(); // Uncaught RangeError: Maximum call stack size exceeded
Copy the code

ES6 super can solve the problem well:

const person = {
    getGreeting() {
        return 'hi! '; }}const friend = {
    getGreeting() {
        return super.getGreeting() + 'my friend'; }}Object.setPrototypeOf(friend, person);

const obj = Object.create(friend);

console.log(obj.getGreeting());  // hi! my friend
Copy the code

About super

The super reference does not change dynamically; it always points to the correct object.

The concept of a “method” was never formally defined before ES6, and a method is simply an object property with functionality rather than data.

In ES6, a method is formally defined as a function that has an internal [[HomeObject]] property that points to the object that the method belongs to.

All references to super are determined by the [[HomeObject]] property:

  1. in[[HomeObject]]Call on propertyObject.getPrototypeOfFind the prototype
  2. Search for the prototype function with the same name and bindthiscall

Class inheritance

In ES6, with the class syntax, inheritance can be implemented using the extends keyword, which is technically more complete than parasitic combinative inheritance.

class SuperType {

    static sayHi() {
        console.log('hi');
    }

    constructor(name) {
        this.name = name;
        this.colors = ["red"."blue"."green"];
    }

    sayName() {
        console.log(this.name); }}class SubType extends SuperType {

    constructor(name, age) {
        super(name);
        this.age = age;
    }

    sayAge() {
        console.log(this.age); }}const sub = new SubType('sub'.18);

sub.sayAge();  / / 18

sub.sayName(); // sub

SubType.sayHi(); // hi
Copy the code

Prototype chain after class inheritance:

Reference Books:

  1. JavaScript Advanced Programming
  2. Deep Understanding of ES6