20170607-Object-Oriented 02-Inheritance

inherit

1 prototype chain

Prototype chain is the main method to realize inheritance. The basic idea is to use prototypes to let one reference type inherit the attributes and methods of another reference type.

Relationships among constructors, prototypes and instances

Each constructor has a prototype object. The prototype object contains a pointer to the constructor, and the instance contains an internal pointer to the prototype object.

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

function SubType(){
    this.subproperty = false;
}
// The essence of an instance that makes SubType inherit SuperType is to rewrite the prototype object
SubType.prototype = new SuperType();
SubType.prototype.getValue = function(){
    return this.subproperty;
}
var instance = new SubType();
alert(instance.getSuperValue());//true


Note: == instance.constructor now points to SuperType, because the original SubType prototype points to another object, the prototype SuperType, and the constructor attribute of this prototype object points to SuperType.==

Default prototype Objec

Determine the relationship between prototypes and examples

  • instanceof: Use this operator to test the constructor that has appeared in the instance and prototype chains, and the result will return true

alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
  • Using the isPrototypeOf() method, as long as the prototype appears in the prototype chain, it can be said to be the prototype of the instance derived from the prototype chain.

alert(Object.prototype isPrototypeOf(instance)); //true
alert(SuperType.prototype isPrototypeOf(instance));
alert(SubType.prototype isPrototypeOf(instance));

When implementing inheritance through prototype chains, you cannot use object literals to create prototype methods, because this will rewrite the prototype chains.

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

function SubType(){
    this.subproperty = false;
}
SubType.prototype = new SuperType();
// Using object literals to create a prototype method rewrites the prototype chain
SubType.prototype = {
    getSubValue: function(){
        return this.subproperty;
    }
};
var instance = new SubType();
alert(instance.getSuperValue());//error!!

The Problem of Prototype Chain

  • Prototype attributes containing reference type values are shared by all instances, so attributes are defined in constructors rather than in prototype objects.
    When inheritance is achieved through prototypes, prototypes actually become instances of another type. Thus, the original instance attributes become the prototype attributes now.

function SuperType(){
    this.color = ["red", "blue", "green"];
    this.name = "Nicholas";
}
function SubType(){
}
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red","blue","green","black"
instance1.name = "Tom";
alert(instance1.name); //"Tom"

var instance2 = new SubType();
// colors are reference types that are shared by all instances
alert(instance2.colors); // "red","blue","green","black"
alert(instance2.name); //"Nicholas"
  • There is no way to pass parameters to supertype constructors without affecting all object instances.

function SuperType(name){
    this.name = name;
}
function SubType(name){
}
// How to pass parameters to supertype constructors?
SubType.prototype = new SuperType();

var instance2 = new SubType(name);
  • In view of the above, prototype chains are seldom used separately in practice.

2. Borrowing constructors

Call the supertype constructor inside the subtype constructor.

function SuperType(){
    this.colors=["red","blue","green"];
}
function SubType(){
    //
    superType.call(this);
}

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

bar instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"

In this way, parameters can be passed to a supertype constructor in a subtype constructor.

function SuperType(name){
    this.name=name;
}
function SubType(){
    Super.call(this,"Nicholas");
    this.age = 29;
}

var instance = new SubType();
alert(instance.name);//"Nicholas"
  • Relevant issues:

There will be the same problem as the constructor pattern - methods are defined in the constructor, and function reuse is out of the question. Also, the methods defined in the supertype prototype are not visible to the subtypes.

3. Composite inheritance - the most commonly used inheritance pattern in JavaScrip

The prototype chain is used to inherit the prototype attributes and methods (function reuse is achieved by defining the method on the prototype), and the instance attributes are inherited by borrowing constructors (ensuring that each instance has its own attributes and can pass parameters to the superclass constructor).

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

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

function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
    alert(this.age);
};

var instance1 = new SubType("Nicholas",29);
instance1.sayName(); //"Nicholas"
instance1.sayAge(); //29

var instance2 = new SubType("Greg",27);
instance2.sayName(); //"Greg"
instance2.sayAge(); //27

4 Prototype Inheritance

With prototypes, new objects can be created based on existing objects without creating custom types.

function object(o){
    // Temporary constructor
    function F(){}
    // Prototype the incoming object as the constructor
    F.prototype = o;
    // Return a new instance
    return new F();
}
var person = {
    name:"Nicholas",
    friends:["shelby","court","van"]
};
// Create a new instance using person as a prototype
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "linda";
yetAnotherPerson.friends.push("barbie");

alert(person.friends); //"shelby,court,van,rob,barbie"

person.friends are not only owned by people, but also shared by anotherPerson and yetAnotherPerson.

ECMAScript5 normalizes prototype inheritance through the Object.create() method, which accepts two parameters: an object used as a prototype for a new object and an object (optionally) that defines additional attributes for a new object. The case where only one parameter is passed in is the same as the object() method.

// Object.create() takes only one parameter
var person = {
    name: "Nicholas",
    friends: ["shelby","court","van"]
};
var anotherPerson = Object.create(person);
// Object.create() takes two parameters
var person = {
    name: "Nicholas",
    friends: ["shelby","court","van"]
};
var anotherPerson = Object.create(person, {
    name:{
        value: "Greg"
    }
});
alert(anotherPerson.name); //Greg

var yetAnotherPerson = Object.create(person, {
    // Define a new friend attribute for a new object that overrides friends in the prototype attribute
    friends:{
        value: ["1","2","3"]
    }
});
alert(yetAnotherPerson.friends); //"1,2,3"
// But the friends attribute in the prototype object is still shared
alert(person.friends); //"shelby,court,van"
alert(anothorPerson.friends); //"shelby,court,van"

5. Parasitic Inheritance

function createAnother(original){
    var clone = object(original);
    // Failure to reuse functions leads to reduced efficiency
    // Add new functions to enhance objects
    clone.sayHi = function(){
        alert("hi");
    }
    return clone;
}

var person = {
    name: "Nicholas",
    friends: ["shelby","court","van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"

6. Parasitic combinatorial inheritance

Composite inheritance calls two supertype constructors in any case: one when creating a prototype of a subtype and the other inside the subtype constructor.

function SuperType(name){
    this.name=name;
    this.colors=["red","blue","green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name,age){
    SuperType.call(this,name);   //The second call to SuperType()
    this.age=age;
}
SubType.prototype = new SuperType(); //The first call to SuperType()

When the SuperType constructor is first called, SubType. prototype (the prototype of SubType) gets two attributes: name and colors. When the SubType constructor is called, the SuperType constructor is called again, this time creating instance properties name and colors on the new object. So these two attributes shield the two identical attributes in the prototype.

To avoid creating the same properties twice, the solution is parasitic combinatorial inheritance. The basic idea is that we don't need to call a supertype constructor to specify a prototype of a subtype. All we need is a copy of the supertype prototype.

function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}

function SuperType(name){
    this.name=name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName=function(){
    alert(this.name);
};
function SubType(name,age){
    SuperType.call(this,name);
    this.age=age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge=function(){
    alert(this.age);
};

The appeal example invokes the SuperType constructor only once, thus avoiding creating unnecessary and redundant attributes on SubType.prototype. Parasitic combinatorial inheritance is the most ideal inheritance paradigm for reference types
By contrast, the second program has less SubType.prototype = new SuperType(); this makes SubType.prototype no name and colors attributes, and avoids creating unnecessary and redundant attributes on SubType.prototype.

Keywords: Javascript Attribute less

Added by nickvd on Tue, 25 Jun 2019 01:17:55 +0300