Prototype chain inheritance
The principle of prototype chain inheritance is very simple. It directly makes the prototype object of the subclass point to the parent instance. When the child instance cannot find the corresponding properties and methods, it will find its prototype object, that is, the parent instance, so as to inherit the properties and methods of the parent class
// Parent class function Parent() { this.name = 'Demi' } // Prototype method of parent class Parent.prototype.getName = function () { return this.name } // Subclass function Child() {} // Let the prototype object of the subclass point to the parent instance, so that the properties and methods that cannot be found in the Child instance will be found on the prototype object (parent instance) Child.prototype = new Parent() Child.prototype.constructor = Child // According to the rules of the prototype chain, bind the constructor by the way. This step does not affect inheritance, but is required when using the constructor // The Child instance can then access the name attribute and getName() method on the parent class and its prototype const child = new Child() child.name // 'Demi' child.getName() // 'Demi'
Disadvantages:
1. Since all Child instance prototypes point to the same Parent instance, modifying the Parent reference type variable of a Child instance will affect all Child instances
2. When creating a subclass instance, you cannot pass parameters to the parent class constructor, that is, you do not implement the function of super()
// Example function Parent() { this.name = ['Demi'] } Parent.prototype.getName = function () { return this.name } function Child() {} Child.prototype = new Parent() Child.prototype.constructor = Child // test const child1 = new Child() const child2 = new Child() child1.name[0] = 'foo' console.log(child1.name) // ['foo'] console.log(child2.name) // ['foo '] (expected to be ['Demi'], the modification of child1.name caused changes to all child instances)
Constructor inheritance
Constructor inheritance, that is, execute the constructor of the parent class in the constructor of the child class, bind the this of the child class, and let the constructor of the parent class hang the member properties and methods to the this of the child class, which can not only avoid sharing a prototype instance between instances, but also pass parameters to the constructor of the parent class
function Parent(name) { this.name = [name] } Parent.prototype.getName = function () { return this.name } function Child() { Parent.call(this, 'Demi') // Execute the parent class construction method and bind the this of the child class, so that the attributes in the parent class can be assigned to the this of the child class } //test const child1 = new Child() const child2 = new Child() child1.name[0] = 'foo' console.log(child1.name) // ['foo'] console.log(child2.name) // ['Demi'] child2.getName() // An error is reported. getName() cannot be found. The constructor cannot inherit the properties and methods on the parent class prototype
Disadvantages: properties and methods on the parent class prototype cannot be found
Combinatorial inheritance
Since prototype chain inheritance and constructor inheritance have complementary advantages and disadvantages, why don't we use them together, so there is a combined inheritance that combines the two
function Parent(name) { this.name = [name] } Parent.prototype.getName = function () { return this.name } function Child() { // Constructor inheritance Parent.call(this, 'Demi') } //Prototype chain inheritance Child.prototype = new Parent() Child.prototype.constructor = Child //test const child1 = new Child() const child2 = new Child() child1.name[0] = 'foo' console.log(child1.name) // ['foo'] console.log(child2.name) // ['Demi'] child2.getName() // ['Demi']
Disadvantages: find that the constructor (Parent.call() and new Parent()) is executed twice every time a subclass instance is created. Although this does not affect the inheritance of the parent class, when a subclass creates an instance, there will be two identical properties and methods in the prototype, which is not elegant
Parasitic inheritance
function Parent(name) { this.name = [name] } Parent.prototype.getName = function () { return this.name } function Child() { // Constructor inheritance Parent.call(this, 'Demi') } //Prototype chain inheritance // Child.prototype = new Parent() Child.prototype = Parent.prototype //Change 'point to parent instance' to 'point to parent prototype'` Child.prototype.constructor = Child //test const child1 = new Child() const child2 = new Child() child1.name[0] = 'foo' console.log(child1.name) // ['foo'] console.log(child2.name) // ['Demi'] child2.getName() // ['Demi']
ES6 inheritance
class Parent { constructor(name) { this.name = name; } getName() {} } class Child extends Parent { constructor(name, age) { super(name); this.age = age; } getAge() {} } let person = new Child('Demi', 24) console.log(person) //
Prototype and prototype chain
In js, we use constructors to create a new object. Each constructor has a prototype attribute value. This attribute value is an object that contains properties and methods that can be shared by all instances of the constructor. When we use the constructor to create a new instance, a pointer will be included inside the instance to point to the value corresponding to the prototype attribute of the constructor. In ES5, this pointer is called the prototype of the object.
When we access the attribute of an object, if the attribute does not exist inside the object, it will go to its prototype object to find the attribute, and the prototype object will have its own prototype, so we keep looking, that is, the concept of prototype chain. The end of the prototype chain is null
Method of obtaining prototype
p.__proto__ p.constructor.prototype Object.getPrototypeOf(p)