Reading notes of JavaScript advanced programming - 8.3 inheritance

Posted by TJMAudio on Tue, 04 Jan 2022 21:47:14 +0100

8.3 succession

8.3.1 prototype chain

Many object-oriented languages support two kinds of inheritance: interface inheritance and implementation inheritance. The former only inherits the method signature, while the latter inherits the actual method. Interface inheritance is not possible in ECMAScript because the function is not signed. Implementation inheritance is the only inheritance method supported by ECMAScript, which is mainly realized through prototype chain.

// Inherit SuperType
SubType.prototype = new SuperType();

SubType. If prototype is an instance of SuperType, its prototype (i.e. SubType.prototype. _proto_) Points to SuperType prototype.

Create its own instance of SubType by assigning a value to its SubType Prototype implements the inheritance of SuperType.

The key to inheritance is that the SubType does not use the default prototype, but replaces it with a new object. This new object happens to be an instance of SuperType.

1 default prototype

By default, all reference types inherit from Object (function), which is also implemented through the prototype chain. The default prototype of any function is an instance of Object, which means that the instance has an internal pointer to Object prototype. As shown in the figure,

2 Relationship between prototype and inheritance

The relationship between prototype and instance can be determined in two ways.

  1. instanceof

    console.log(instance instanceof Object); // true
    console.log(instance instanceof SuperType); // true
    console.log(instance instanceof SubType); // true
    
  2. isPrototypeOf

    console.log(Object.prototype.isPrototypeOf(instance)); // true
    console.log(SuperType.prototype.isPrototypeOf(instance)); // true
    console.log(SubType.prototype.isPrototypeOf(instance)); // true
    
3 about methods
4 problems of prototype chain
  1. When the prototype contains a reference value, it is similar to creating a SubType prototype. colors property, which is shared by all instances of SubType.
  2. A subtype cannot pass arguments to the constructor of a parent type when instantiated. Because the prototype of the subtype has been constructed.

8.3.2 stealing constructor steaming

Sometimes called "object camouflage" or "classic inheritance". The basic idea is simple: call the parent class constructor in the subclass constructor.

function SuperType() {
    this.colors = ["red", "blue", "green"];
}
function SubType() {
    // Inherit SuperType
    SuperType.call(this);
}
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"

let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green"
1 transfer parameters

One advantage of stealing constructors over using prototype chains is that you can pass parameters to the parent constructor in the child constructor.

function SuperType(name){
    this.name = name;
}
function SubType() {
    // Inherit SuperType and pass parameters
    SuperType.call(this, "Nicholas");
    // Instance properties
    this.age = 29;
}

let instance = new SubType();
console.log(instance.name); // "Nicholas";
console.log(instance.age); // 29
2. The problem of stealing constructors

The main disadvantage of stealing constructors is also the problem of using constructor mode to customize types:

  • Methods must be defined in constructors, so functions cannot be reused
  • Subclasses also cannot access methods defined on the parent class prototype, so all types can only use the constructor pattern

8.3.3 combination inheritance

Combinatorial inheritance combines the prototype chain and stealing constructor, and centralizes their advantages. The basic idea is to use the prototype chain to inherit the properties and methods on the prototype, and inherit the instance properties by stealing the constructor. In this way, the method can be defined on the prototype to realize reuse, and each instance can have its own properties.

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

function SubType(name, age){
    // Inheritance properties
    SuperType.call(this, name);
    this.age = age;
}
// Inheritance method
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {
    console.log(this.age);
};

let instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Nicholas";
instance1.sayAge(); // 29

let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27

Composite inheritance makes up for the shortcomings of prototype chain and stealing constructor. It is the most used inheritance mode in JavaScript. Moreover, composite inheritance also retains the ability of the instanceof operator and isProtptypeOf() method to identify composite objects.

8.3.4 prototype inheritance

This article introduces an inheritance method that does not involve constructors in the strict sense. His starting point is that information sharing between objects can be realized through prototypes even without custom types.

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

ECMAScript 5 by adding object The create () method normalizes the concept of prototype inheritance. This method takes two parameters: the object that is the prototype of the new object and the object that defines additional properties for the new object (the second optional). When there is only one parameter, object Create () has the same effect as the object() method here:

let person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

let yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"

Object. The second parameter of create () is the same as object The second parameter of defineproperties () is the same: each new property is described by its own descriptor. Properties added in this way mask properties with the same name on the prototype object. For example:

let person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = Object.create(person, {
    name: {
    	value: "Greg"
    }
});
console.log(anotherPerson.name); // "Greg"

Prototype inheritance is ideal for situations where you do not need to create a separate constructor, but still need to share information between objects. Remember, however, that the reference values contained in the properties are always shared among related objects, just like using the prototype pattern.

8.3.5 parasitic inheritance

The idea behind parasitic inheritance is similar to parasitic constructor and factory pattern: create a function that implements inheritance, enhance the object in some way, and then return the object.

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

Note that adding functions to objects through parasitic inheritance makes functions difficult to reuse, similar to the constructor pattern.

Composite inheritance has efficiency problems. The parent constructor will always be called twice.

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); // The second call to SuperType()
    this.age = age;
}

SubType.prototype = new SuperType(); // SuperType() called for the first time
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
    console.log(this.age);
};

8.3.6 parasitic combined inheritance

Parasitic composite inheritance inherits properties by stealing constructors, but uses hybrid prototype chain inheritance methods. The basic idea is to get a copy of the parent class prototype instead of assigning a value to the child class prototype by calling the parent class constructor. The basic mode is as follows:

function inheritPrototype(subType, superType) {
    let prototype = object(superType.prototype); // create object
    prototype.constructor = subType; // Enhanced object
    subType.prototype = prototype; // Assignment object
}
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);
    this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function() {
    console.log(this.age);
};

Parasitic composite inheritance is the best mode of reference type inheritance.

Topics: Javascript inheritance