Sort out the advantages and disadvantages of inherited methods in javascript Advanced Programming.

1. The prototype chain

The concept of stereotype chains is described in ECMAScript as the primary method of implementing inheritance.

The basic idea of stereotype chain inheritance is to use stereotypes to make one reference type inherit the properties and methods of another.

A quick review of the relationship between constructors, stereotypes, and instances: Each constructor has a stereotype object, which contains a pointer to the constructor, and each instance contains an internal pointer to the stereotype object.

So what if we made the prototype object equal to an instance of another type? Obviously, the stereotype object will contain a pointer to another stereotype, which in turn will contain a pointer to another constructor. If another prototype is an instance of another type, then the above relationship still holds, and so on, the chain of instance and prototype is formed. This is the basic concept of the so-called prototype chain.

function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function () {
    return this.property;
}
function SubType() {
    this.subProperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
    return this.subproperty;
};

var instance = new SubType();
console.log(instance.getSuperValue());      //true
Copy the code

The code above defines two types: SuperType and SubType. Each type has an attribute and a method. The main difference is that SubType inherits from SuperType, which is achieved by creating an instance of SuperType and assigning that instance to subType.prototype. The essence of the implementation is to rewrite the prototype object and replace it with an instance of a new type. In other words, all the properties and methods that existed in the SuperType instance now also exist in subType.prototype. After the inheritance relationship is established, we add a method to subtype. prototype, which adds a new method to the properties and methods inherited from SuperType

Constructor now points to SuperType. This is because the constructor from SubType. Prototype was overwritten. In fact, it is not the constructor property of the stereotype of SubType that has been overridden, but rather that the stereotype of SubType points to another object, the stereotype of SuperType, whose constructor property points to SuperType

Don’t forget the default stereotype in fact, there is one link missing from the prototype chain shown in the previous example. As we know, all reference types inherit from Object by default, and this inheritance is also implemented through the stereotype chain. Remember that the default prototype for all functions is an instance of Object, so the default prototype will contain an internal pointer to Object.prototype. This is why all custom types inherit default methods like toString() and valueOf()

Problems with prototype chains While prototype chains are powerful and can be used for inheritance, they have some problems. Of these, the main problem comes from stereotypes that contain reference type values

function SuperType(){
    this.colors = ["red"."blue"."green"];
}

function SubType(){}// Inherits SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);        //"red,blue,green,black"

var instance2 = new SubType();
console.log(instance2.colors);        //"red,blue,green,black"
Copy the code

The second problem with stereotype chains is that you cannot pass arguments to the constructor of a supertype when creating instances of a subtype. In fact, there is no way to pass arguments to a supertype constructor without affecting all object instances. Given this, and the problems just discussed with stereotypes containing reference type values, stereotype chains alone are rarely used in practice

2. Borrow constructors

The supertype constructor is called inside the subtype constructor

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

var instance2 = new SubType();
console.log(instance2.colors);    //"red,blue,green"
Copy the code

By using the call() method (or apply(), we are actually calling the SuperType constructor in the context of a newly created SubType instance that will be created in the future. This will execute all the object initialization code defined in the SuperType() function on the new SubType object. As a result, each instance of SubType has its own copy of the Colors attribute

For stereotype chains, borrowing constructors has the great advantage of being able to pass arguments to supertype constructors in subtype constructors

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

function SubType(){  
    // Inherits SuperType and also passes arguments
    SuperType.call(this."Nicholas");

    // Instance properties
    this.age = 29;
}

var instance = new SubType();
console.log(instance.name);    //"Nicholas";
console.log(instance.age);     / / 29
Copy the code

Borrowing constructors problem: Methods are defined in constructors, so function reuse is out of the question. Furthermore, methods defined in the stereotype of a supertype are also invisible to subtypes, resulting in only the constructor schema being used for all types

3. Combinatorial inheritance

Combination inheritance, sometimes called pseudo-classical inheritance, refers to an inheritance pattern that combines a chain of archetypes and techniques borrowed from constructors to take advantage of the best of both. The idea behind this is to use stereotype chains to inherit stereotype attributes and methods, while borrowing constructors to inherit instance attributes. In this way, function reuse is achieved by defining methods on prototypes, while ensuring that each instance has its own attributes

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

SuperType.prototype.sayName = function(){
    console.log(this.name);
};

function SubType(name, age){  

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

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

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

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

Composite inheritance avoids the defects of stereotype chains and borrowed constructors, and combines their advantages to become the most common inheritance pattern in JavaScript. Furthermore, Instanceof and isPrototypeOf can also be used to identify objects created based on composite inheritance.

In any case, the supertype constructor is called twice: once when the subtype stereotype is created and once inside the subtype constructor

4. Original type inheritance

This approach does not use a strict constructor. Stereotypes allow you to create new objects based on existing objects without having to create custom types

function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
var person = {
    name: "Nicholas".friends: ["Shelby"."Court"."Van"]};var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

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

ECMAScript 5 normalizes primitive inheritance by adding the object.create () method. This method takes two parameters: an object to be used as a prototype for the new object and, optionally, an object that defines additional properties for the new object. Object.create() behaves the same as the Object () method when passed a parameter.

The second argument to the object.create () method has the same format as the second argument to the Object.defineProperties() method: each property is defined by its own descriptor. Any property specified in this way overrides the property of the same name on the prototype object

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

Old-style inheritance is perfectly fine in cases where there is no need to create constructors, but just want one object to be similar to another. Remember, however, that attributes that contain reference type values will always share corresponding values, just as with the stereotype pattern

Parasitic inheritance

Create a function that simply encapsulates the inheritance process, internally enhances the object in some way, and finally returns the object as if it really did all the work

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

In this example, the createAnother() function takes an argument, which is the object that will be the basis for the new object. This object(Original) is then passed to the object() function, which assigns the result to Clone. Add a new method sayHi() to the Clone object and return it. You can use createAnother() like this:

var person = {
    name: "Nicholas".friends: ["Shelby"."Court"."Van"]};var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
Copy the code

The code in this example returns a new object, anotherPerson, based on Person. The new object not only has all the attributes and methods of Person, but also has its own sayHi() method

Using parasitic inheritance to add functions to objects will reduce efficiency because functions cannot be reused. This is similar to the constructor pattern

Parasitic combinatorial inheritance

As mentioned earlier, composite inheritance is the most common inheritance pattern in JavaScript; However, it has its own disadvantages. The biggest problem with composite inheritance is that in any case, the supertype constructor is called twice: once when the subtype stereotype is created and once inside the subtype constructor. Yes, subtypes eventually contain all of the instance properties of the supertype object, but we have to override those properties when we call the subtype constructor. Take a look at the following composite inheritance example

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);         // Call SuperType() for the second time

    this.age = age;
}

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

When the SuperType constructor is first called, subtype. prototype gets two attributes: name and colors; They are all instance properties of SuperType, but are now in the stereotype of SubType. When the SubType constructor is called, the SuperType constructor is called again, this time creating the instance attributes name and colors on the new object, thus masking the two attributes of the same name in the stereotype

Parasitic combinatorial inheritance inherits properties by borrowing constructors and inherits methods through a hybrid form of prototype 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. Essentially, you use parasitic inheritance to inherit the stereotype of the supertype and then assign the result to the stereotype of the subtype. The basic pattern of parasitic combinatorial inheritance is shown below

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

The inheritPrototype() function in this example implements the simplest form of parasitic combinatorial inheritance. This function takes two arguments: a subtype constructor and a supertype constructor. Inside the function, the first step is to create a copy of the supertype stereotype. The second step is to add the Constructor attribute to the created copy to make up for the loss of the default constructor attribute by rewriting the stereotype. Finally, the newly created object (that is, the copy) is assigned to the prototype of the subtype. This way, we can replace the assignment of subtype stereotypes in the previous example with statements that call the inheritPrototype() function

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);
};
Copy the code

The efficiency of this example is that it calls the SuperType constructor only once and thus avoids creating unnecessary, redundant attributes on subType. prototype. At the same time, the prototype chain stays the same; Therefore, instanceof and isPrototypeOf() can also be used normally. Parasitic combinatorial inheritance is generally considered by developers to be the ideal inheritance paradigm for reference types.