• My blog

  • The Github homepage


A prototype object

Whenever a new function is created, a Prototype property is created for the function according to a specific set of rules, which points to the function’s prototype object. By default, all prototype objects automatically get a constructor attribute that points to the function where the Prototype attribute is located.

function Person(){}Copy the code

When we create an instance with a constructor, we also create a __proto__ property for that instance, which is a pointer to the constructor’s prototype object

let person = new Person();
person.__proto__ === Person.prototype    // true
let person1 = new Person();
person1.__proto__ === Person.prototype    // true
Copy the code

As a result of all instances of the same constructor creates __proto__ attribute points to the constructor of a prototype object, so all the instance objects will share the prototype of the constructor object on all the attributes and methods, once the prototype on the object properties or methods change, all instances of the object will be affected.

function Person(){
}
Person.prototype.name = "Luke";
Person.prototype.age = 18;
let person1 = new Person();
let person2 = new Person();
alert(person1.name)    // "Luke"
alert(person2.name)    // "Luke"
Person.prototype.name = "Jack";
alert(person1.name)    // "Jack"
alert(person2.name)    // "Jack"
Copy the code

Overwrite prototype object

We often rewrite the entire prototype object with an object literal containing all properties and methods, as shown in the following example

function Person(){
}
Person.prototype = {
    name : "Luke".age : 18.job : "Software Engineer".sayName : function(){
        alert(this.name)
    }
}
Copy the code

In the above code, we set Person.prototype to a new Object that has no constructor property, which causes the constructor property to point to Object instead of Person.

let friend = new Person();
alert(friend.constructor  === Person);    //false 
alert(friend.constructor  === Object);    //true
Copy the code

If the constructor value is important, we can deliberately set it back to the appropriate value as follows

function Person(){
}
Person.prototype = {
    constructor : Person,
    name : "Luke".age : 18.job : "Software Engineer".sayName : function(){
        alert(this.name)
    }
}
Copy the code

Prototype chain and prototype chain inheritance

Each constructor has a prototype object, which contains a pointer to the constructor, and each instance contains an internal pointer to the prototype object (__proto__). 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 the other stereotype is an instance of another constructor, then the relationship still holds, and so on, forming a chain of instance and stereotype. This is the basic concept of the so-called prototype chain.

function Super(){
    this.property = true;
}

Super.prototype.getSuperValue = function(){
    return this.property;
}

function Sub(){
    this.subproperty = false;
}

Sub.prototype = new Super();    // Inherits Super

Sub.prototype.getSubValue = function (){
    return this.subproperty;
}

let instance = new Sub();
console.log(instance.getSuperValue());    //true

console.log(instance.__proto__ === Sub.prototype);    //true
console.log(Sub.prototype.__proto__ === Super.prototype);    //true

Copy the code

Sub.prototype = new Super(); Inheritance is implemented by creating an instance of Super and assigning that instance to sub. prototype. All properties and methods that are present in the Super instance and the prototype object at this point are also present in sub.prototype. Instanse’s __proto__ property points to Sub. Prototype, which in turn points to Super. Prototype.

Prototype chain search mechanism

When an instance property is accessed, the property is first searched in that instance. If the property is not found, the search continues for the prototype of the instance. In the case of inheritance through the prototype chain, the search process can continue to search up the prototype chain until the attribute is found, or search to the highest prototype chain Object. Prototype, still not found, return undefined. In the example above, calling instance.getsupervalue () takes three search steps: 1) To search for instances; 2) Search sub.prototype; 3) Search for super.prototype and find the method in the last step. In the absence of a property or method, the search process always stops at the end of the prototype chain.

Prototype chain problem

The biggest problem with stereotype chain inheritance comes from stereotypes that contain reference type values. Stereotype attributes that reference type values are shared by all instances. This is why you define attributes in constructors rather than prototype objects. When inheritance is implemented through a stereotype, the stereotype is actually an instance of another type. Thus, the original instance property becomes the prototype property.

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

}
Sub.prototype = new Super();    // Inherits Super

let instance1 = new Sub();

instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"

let instance2 = new Sub();
alert(instance2.colors);    //"red, blue, green, black"
Copy the code

In the code above, the Super constructor defines a colors attribute, which is an array. Each instance of Super has a colors attribute that contains its own array. When Sub inherits Super through the prototype chain, sub. prototype becomes an instance of Super and thus has its own colors attribute. The result is that all Sub instances share the colors attribute. The second problem with prototype chains is that there is no way to pass arguments to the constructor of a superclass without affecting all object instances.

Constructor inheritance (classical inheritance)

That is, the parent constructor is called in the subclass constructor, and when an instance of a subclass is built, that instance also has the attributes and methods of the parent instance.

function Super(){
    this.colors = ["red"."blue"."green"];
}
function Sub(){
    Super.call(this, name);    // Inherits Super
}

let instance1 = new Sub();

instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"

let instance2 = new Sub();
alert(instance2.colors);    //"red, blue, green"
Copy the code

The above code also calls the constructor of Super when building an instance of Sub, which executes all the object initialization code defined in the Super() function on the new Sub object. As a result, each instance of Sub has its own copy of the Colors attribute.

Constructor inheritance issues

If you just borrow constructors, you can’t avoid the problem of the constructor pattern — methods are defined in constructors, so function consumption is out of the question. Also, methods defined in the superclass stereotype are invisible to subclasses only.

Combination of inheritance

It is a kind of inheritance pattern that combines the prototype chain and the constructor to play the best of the two. The idea is to inherit stereotype attributes and methods using stereotype chains, and to inherit instance attributes by borrowing constructors. In this way, function reuse is achieved by defining methods on prototypes, while ensuring that each instance has its own attributes.

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

Super.prototype.sayName = function (){
    alert(this.name);
};

function Sub(name, age){
    Super.call(this, name);    // Inherit the Super attribute (call Sup constructor for the second time)
    this.age = age;
}

Sub.prototype = new Super();    // Inherits the method on the Super prototype chain (first call to the Sup constructor)
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function (){
    alert(this.age);
};

var instance1 = new Sub("Luke".18);
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    / / 18

var instance2 = new Sub("Jack".20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    / / 20

Copy the code

In the example above, the Sup constructor defines two attributes: name and colors. The Sup prototype defines a method sayName(). The Sub constructor calls the Sup constructor with the name argument and then defines its own property, age. We then assign an instance of Sup to a prototype of Sub, and then define the sayAge() method on that new prototype. This allows two different Sub instances to use the same method, each with its own attributes ————, including the colors attribute. Composite inheritance avoids the defects of stereotype chains and constructors and combines their advantages. It is the most commonly used inheritance pattern in JavaScript. The fly in the ointment, however, is that the superclass constructor is called twice in the code above. Sub.prototype = new Super(); The first call to the Sup constructor assigns an instance of the Sup constructor to the Sub subclass’s prototype object sub.prototype. The attributes on the parent constructor instance are also assigned to the sub. prototype object of the subclass. The second call to the super.call (this) constructor in the subclass assigns attributes from the instance of the superclass constructor to the instance of the subclass constructor. According to the stereotype chain search principle, attributes on the instance mask attributes on the stereotype chain. Therefore, there is no need to assign attributes of the parent constructor instance to the prototype object of the child class, which is wasteful and pointless.

Optimized combinatorial inheritance

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

Super.prototype.sayName = function (){
    alert(this.name);
};

function Sub(name, age){
    Super.call(this, name);    // Inherits the Super attribute
    this.age = age;
}

function F(){
}
F.prototype = Super.prototype; 
Sub.prototype = new F();    // Inherits the methods on the Super prototype chain

Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function (){
    alert(this.age);
};

var instance1 = new Sub("Luke".18);
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    / / 18

var instance2 = new Sub("Jack".20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    / / 20

Copy the code

The above example accomplishes prototype chain inheritance by assigning the parent class’s prototype object directly to an intermediate constructor’s prototype object, and then assigning the intermediate constructor instance to the sub. prototype of the subclass. It is efficient in that only one superclass constructor, Super, is called and the prototype chain remains the same. An easier way to write this is to use ES5’s object.create () method instead of the intermediate constructor, but the principle is the same

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

Super.prototype.sayName = function (){
    alert(this.name);
};

function Sub(name, age){
    Super.call(this, name);    // Inherits the Super attribute
    this.age = age;
}
/* function F(){ } F.prototype = Super.prototype; Sub.prototype = new F(); / / inherited the Super prototype chain methods on the Sub. The prototype. The constructor = Sub; * /
// This line of code works the same way as the code commented above
Sub.prototype = Object.create(Super.prototype, {constructor: {value: Sub}})

Sub.prototype.sayAge = function (){
    alert(this.age);
};

var instance1 = new Sub("Luke".18);
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    / / 18

var instance2 = new Sub("Jack".20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    / / 20
Copy the code

Easier inheritance

A simpler method of inheritance is to direct __proto__ from the protoclass’s prototype object to its parent’s prototype object. This method does not change the protoclass’s prototype object, so the constructor property on the protoclass object points to the protoclass’s constructor. And when an instance of a subclass does not find the corresponding attribute or method on the subclass’s prototype object, it continues to search for the attribute or method on the parent class’s prototype object using the __proto__ attribute on the subclass’s prototype object

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

Super.prototype.sayName = function (){
    alert(this.name);
};

function Sub(name, age){
    Super.call(this, name);    // Inherits the Super attribute
    this.age = age;
}

Sub.prototype.__proto__ = Super.prototype
Sub.prototype.sayAge = function (){
    alert(this.age);
};
var instance1 = new Sub("Luke".18);
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    / / 18

var instance2 = new Sub("Jack".20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    / / 20
Copy the code

Object.setPrototypeOf()

Object.setprototypeof () is a method in the latest draft of ECMAScript 6 that is considered a more appropriate way to modify Object prototypes than Object.prototype.proto

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

Super.prototype.sayName = function (){
    alert(this.name);
};

function Sub(name, age){
    Super.call(this, name);    // Inherits the Super attribute
    this.age = age;
}

//Sub.prototype.__proto__ = Super.prototype
Object.setPrototypeOf(Sub.prototype, Super.prototype)

Sub.prototype.sayAge = function (){
    alert(this.age);
};
var instance1 = new Sub("Luke".18);
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    / / 18

var instance2 = new Sub("Jack".20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    / / 20
Copy the code

Class static method inheritance

None of the above inheritance methods implement static method inheritance of a class, whereas in ES6 class inheritance, subclasses can inherit static methods of their parent class. We can implement static method inheritance of a class with object.setPrototypeof (), which is very simple

Object.setPrototypeOf(Sub, Super)
Copy the code
function Super(name){
    this.name = name;
    this.colors = ["red"."blue"."green"];
}

Super.prototype.sayName = function (){
    alert(this.name);
};

Super.staticFn = function(){
    alert('Super.staticFn')}function Sub(name, age){
    Super.call(this, name);    // Inherits the Super attribute
    this.age = age;
}

//Sub.prototype.__proto__ = Super.prototype
Object.setPrototypeOf(Sub.prototype, Super.prototype)
Object.setPrototypeOf(Sub, Super)    // Inherits a static attribute or method from the parent class
Sub.staticFn()    // "Super.staticFn"

Sub.prototype.sayAge = function (){
    alert(this.age);
};
var instance1 = new Sub("Luke".18);
instance1.colors.push("black");
alert(instance1.colors);    //"red, blue, green, black"
instance1.sayName();    //"Luke"
instance1.sayAge()    / / 18

var instance2 = new Sub("Jack".20);
alert(instance2.colors);    //"red, blue, green"
instance2.sayName();    //"Jack"
instance2.sayAge()    / / 20
Copy the code

This is probably the ideal inheritance in the end.