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.