JavaScript inherit several ways to achieve

Foreword

Inheritance, as an effective means to reuse code, is of great significance in object-oriented programming. But this scripting language does not like some static language that provides class-based inheritance to achieve the true sense, but uses a prototype-based inheritance . Here to talk about when ES5, use JavaScript to achieve inheritance in several ways.
Before Specifically these ways, the first pre-clear a few concepts.

Function: In JavaScript, on each will generally have a function prototype object, if we use this new function through the operator, when positioned like a constructor function, corresponds to the class that provides a template, and At this point the prototype object is to describe the template.

Prototype chain: When we try to access a property or method instance. If there is no current instance, it will be entrusted to his prototype object. If or not, it will always follow the prototype chain looking up, until Object.prototype. Now the mainstream browsers provide a __proto__ to find his prototype object. Or you can use Object.getPrototypeOf to get.

1) prototype inheritance chain.

// 基于原型链继承
function Parent(name){
    this.name = name;
}

Parent.prototype.getName = function(){
    return this.name;
}

Parent.prototype.foo = ["parent"];



function Child(){
}

Child.prototype = new Parent();

const parent = new Parent('parent')
const child = new Child('child');
parent.foo.push('hello');
console.log(parent.foo);  // [ 'parent', 'hello' ]
console.log(child.foo); // [ 'parent', 'hello' ]

console.log(child.name); // undefined

console.log(parent instanceof Parent); //true
console.log(child instanceof Parent);  //true
console.log(child instanceof Child);  //true


This code can explain a few questions.

  1. If there is reference to the prototype object attributes, as long as any instance of a change in the content of this reference to the data structure, another example will be affected at the time of the visit, of course, most of the time this is not what we need, we just want each instance there the same property only.
  2. When we instantiate Child, printed name is undefined, of course, because this constructor Child already did not use the name attribute, but we hope that this can be multiplexed Parent constructor, rather than in subclass constructor reactor to write again.
  3. In this case, child instance that already belongs Parent "parent", since the prototype chain through "string" up between them. The final determination is equivalent to the following code instanceof
console.log(parent.__proto__ == Parent.prototype);
console.log(child.__proto__.__proto__ == Parent.prototype);
console.log(child.__proto__ == Child.prototype);

2) the parent class constructor method call the constructor subclass.

// 借用父类构造器函数
function Parent(name,age){
    this.name = name;
    this.age = age;
    this.role = 'parent';
}

Parent.prototype.getName = function(){
    return this.name;
}

function Child(){
    const args = Array.prototype.slice.call(arguments)
    Parent.apply(this,args)
    this.role = 'child';
}

const parent = new Parent('p',50);
const child = new Child('c',20);

console.log(parent.name); // p
console.log(parent.age); //50
console.log(parent.getName()); // 50

console.log(child.name); //c
console.log(child.age); //20
console.log(child.getName && child.getName()); // undefined

console.log(parent instanceof Parent); //true
console.log(child instanceof Parent); // false
console.log(child instanceof Child); // true
  1. First, now each instance has its own properties, and the Child constructor call parent class constructor can be multiplexed into the code portion
  2. In the instance of a subclass of getName want to use this method, in fact, it can not be found. At this time, since in this method the prototype Parent constructor. Of course, you can put this statement into the constructor method, or re-apply once on the Child this prototype, but this would once again fall into the cycle of code redundancy.
  3. At this child instanceof Parent returns false, we still hope that he returns true

3) The prototype chain inherit the parent class constructor and borrowing combined, the combined inheritance

// 借用父类构造器函数
function Parent(name,age){
    this.name = name;
    this.age = age;
    this.role = 'parent';
}

Parent.prototype.getName = function(){
    return this.name;
}

function Child(){
    const args = Array.prototype.slice.call(arguments)
    Parent.apply(this,args)
    this.role = 'child';
}

Child.prototype = new Parent();

const parent = new Parent('p',50);
const child = new Child('c',20);

console.log(parent.name); //p
console.log(parent.age); //50
console.log(parent.getName()); //p
console.log(parent.role); //parent


console.log(child.name); //c
console.log(child.age); //20
console.log(child.getName && child.getName()); //c
console.log(child.role); //child

console.log(parent instanceof Parent); true
console.log(child instanceof Parent); // true
console.log(child instanceof Child); // true
  1. At this point, we have found that this inheritance has solved many of the problems mentioned above, there is something up
  2. But a closer look reveals that, when we realize this inheritance, the parent class constructor is called twice. If the constructor of the parent class is very complex, and that such operation is not what we want.

4) prototypal inheritance

// 原型式继承
const foo = {
    name:'xxx',
    age:28,
    getName(){
        return this.name;
    }
};


const clone = Object.create(foo);

console.log(clone.name); // xxx
console.log(clone.age); // 28
console.log(clone.getName()); // xxx
 

This should distinguish the difference between inheritance and prototype chain.

  1. It can be seen before the inheritance is the first to have a parent class's constructor, and then trying to go subclass inherits him to achieve code reuse. And here, we Object.create through this API, can achieve code reuse. Implement this API is equivalent to the following clone function.
function clone(obj){
    var F = function(){};
    F.prototype= obj;
    return new F();
}
  1. Here is not so much "inheritance", as it is apt to clone. We direct return an instance of the anonymous constructor, prototype object of this example points to a target object, so that when we went to visit a property in this instance, we will go to entrust the specified object.
  2. From the clone function implementation point of view, if we have passed obj reference type attribute, multiple instances will share him, there will be problems affect each other.

5) parasitic inherit
the prototypal inheritance nature or use a shallow copy of the object to achieve code reuse, but our goal target after target, if there is a reference attribute, cloning can access to this property, if we want every cloned object has its own attributes may wish to do so.

// 寄生式继承
const foo = {
    name:'xxx',
    age:28,
    getName(){
        return this.name;
    }
};

function cloneDecorator(obj){
    const clone = Object.create(obj);
    clone.selfAttributes = ['left','right'];
    clone.getSelfAttrs = function(){
        return this.selfAttributes;
    }
    return clone;
}

const obj1 = cloneDecorator(foo);
const obj2 = cloneDecorator(foo);
obj1.selfAttributes.push(1);
obj2.selfAttributes.push(2);
console.log(obj1.getName()); // xxx
console.log(obj1.getSelfAttrs()); // [ 'left', 'right', 1 ]
console.log(obj2.getName()); // xxx
console.log(obj2.getSelfAttrs()); // [ 'left', 'right', 2 ]

  1. cloneDecorator This method is actually a little taste of the decorator pattern, we return to this example in addition to anonymous constructor addition, he also made some additional property enhancements, of course, back to the problems encountered here before, getSelfAttrs this method each instance are declared once. But if the object into foo, will produce a plurality of interacting problem instance.
  2. But prototypal inheritance and succession Parasitic offer a new idea, we want to string up the prototype chain, do not necessarily have to call the constructor of the parent class, we can direct light prototype copy the parent class constructor. This leads to one of the following inheritance implementation.

6) combined parasitic inheritance.
The above-mentioned 3 and 5 together, we can write the following code

// 借用父类构造器函数
function Parent(name,age){
    this.name = name;
    this.age = age;
    this.role = 'parent';
}

Parent.prototype.getName = function(){
    return this.name;
}

function Child(){
    const args = Array.prototype.slice.call(arguments)
    Parent.apply(this,args)
    this.role = 'child';
}

function inherit(subType,superType) {
    const subTypePrototype = Object.create(superType.prototype);
    subTypePrototype.constructor = subType;
    subType.prototype = subTypePrototype;
}

inherit(Child,Parent)

const parent = new Parent('p',50);
const child = new Child('c',20);

console.log(parent.name);
console.log(parent.age);
console.log(parent.getName());
console.log(parent.role);


console.log(child.name);
console.log(child.age);
console.log(child.getName && child.getName());
console.log(child.role);

console.log(parent instanceof Parent);
console.log(child instanceof Parent);
console.log(child instanceof Child);

You can see the printed result is the same and the combined inheritance, inherit function to replace the original new operation. This inheritance is relatively integrate a variety of inherited advantages.

Talk about class inheritance

In ES6 provided a syntactic sugar such class, pay attention to just syntactic sugar, JavaScript is a prototype-based inheritance to achieve. We can look up at the babel, extends a class of what has been done.

Here Insert Picture Description
What is not evident.

Published 44 original articles · won praise 21 · views 50000 +

Guess you like

Origin blog.csdn.net/u014298440/article/details/104889438