Inheritance

Prototype Chaining

  Recall the relationship between consructors, prototypes and instances: each constructor has a prototype object that points back to the constructor, and instances have an internal pointer to the prototype. What if the prototype were actually an instance of another type? That would mean the prototype itself would have a pointer to a different prototype that, in turn, would have a pointer to another constructor. If that prototype were also an instance of another type, then the pattern would continue, forming a chain between instances and prototypes. This is the basic idea behind prototype chaining.

  Implement prototype chaining involves the following code pattern:

 1 function SuperType(){
 2     this.property = true;
 3 }
 4 
 5 SuperType.prototype.getSuperValue = function(){
 6     return this.property;
 7 }
 8 
 9 function SubType(){
10     this.subproperty = false;
11 }
12 
13 // inherit from SuperType
14 SubType.prototype = new SuperType();
15 
16 SubType.prototype.getSubValue = function(){
17     return this.subproperty;
18 };
19 
20 var instance = new SubType();
21 alert(instance.getSuperValue());                      // true
View Code

 

Problems with Prototype Chaining

  The major issue revolves around prototypes that contain reference values. When implementing inheritance using prototypes, the prototype actually becomes an instance of another type, meaning that what once were instance properties are now prototype properties. This issue is highlighted by the following example:

 1 function SuperType(){
 2     this.colors = ["red", "blue", "green"];
 3 }
 4 
 5 function SubType(){}
 6 
 7 // inherit from SuperType
 8 SubType.prototype = new SuperType();
 9 
10 var instance1 = new SubType();
11 instance1.colors.push("black");
12 alert(instance1.colors);                          // "red, blue, green, black"
13  
14 var instance2 = new SubType();
15 alert(instance2.colors);                          // "red, blue, green, black"
View Code

 

Constructor Stealing

  The basic idea is quite simple: call the supertype constructor from within the subtype constructor. Keeping in mind that functions are simple objects that execute code in a particular context, the apply() and call() methods can be used to execute a constructor on the newly created object, as in this example:

 1 function SuperType(name){
 2     this.name = name;
 3 }
 4 
 5 function SubType(){
 6     // inherit from SuperType passing in an argument
 7     SuperType.call(this, "Nicholas");
 8 
 9     // instance property
10     this.age = 29;
11 }
12 
13 var instance = new SubType();
14 alert(instance.name);                     // "Nicholas"
15 alert(instance.age);                        // 29
View Code

 Problems with Constructor Stealing

  The downside to using constructor stealing exclusively is that it introduces the same problem as the constructor pattern for custom types: methods must be defined inside the constructor, so there's no function reuse. Furthermore, methods defined on the supertype's prototype are not accessible on the subtype, so all types can use only the constructor pattern. 

 

Combination Inheritance

  The basic idea is to use prototype chaining to inherit properties and methods on the prototype and to use constructor stealing to inherit instance properties. This allows function reuse by defineing methods on the prototype and allows each instance to have its own properties. Consider the following: 

 1 function SuperType(name){
 2     this.name = name;
 3     this.colors = ["red", "blue", "green"];  
 4 }
 5 
 6 SuperType.prototype.sayName = function(){
 7     alert(this.name);
 8 };
 9 
10 function SubType(name, age){
11     SuperType.call(this, name);
12     this.age = age;
13 }
14 
15 SubType.prototype = new SuperType();
16 
17 SubType.prototype.sayAge = function(){
18     alert(this.age);
19 };
20 
21 var instance1 = new SubType("Nicholas", 29);
22 instance1.color.push("black");
23 alert(instance1.colors);           // "red, blue, green, black";
24 instance1.sayName();               // "Nicholas";
25 instance1.sayAge();                // 29;
26 
27 var instance2 = new SubType("Greg", 27);
28 alert(instance2.colors);            // "red, blue, green";
29 instance2.sayName();                // "Greg";
30 instance2.sayAge();                 // 27
View Code

 

Prototypal inheritance

   Object.create() method accepts two arguments, an object to use as the prototype for a new object and an optional object defining additional properties to apply to the new object.

 1 var person = {
 2     name:  "Nicholas",
 3     friends:  ["Shelby", "Court", "Van"]  
 4 };
 5 
 6 var anotherPerson = Object.create(person, {
 7     name:  {
 8         value: "Greg"
 9     }
10 });
11 
12 anotherPerson.friends.push("Rob");
13 
14 alert(anotherPerson.name);                       // "Greg"
15 alert(anotherPerson.friends);                      // "Shelby, Court, Van, Rob"
View Code

  Prototypal inheritance is useful when there is no need for the overhead of creating separate constructor, but you still need an object to behave similiarly to another. Keep in mind that properties containing reference values will always share those values, similar to using the prototype pattern.

 

Parasitic Inheritance

  The idea behind parasitic inheritance is similar to that of the parasitic constructor and factory patterns: create a funciton that does the inheritance, augments the object in some way and then returns the objects as if it did all the work. The basic parasitic inheritance pattern looks like this:

1 function createAnother(original){
2     var clone = object(original);               // create a new object by calling a function
3     clone.sayHi = function(){                  // augment the object in some way
4         alert("hi");
5     };
6     return clone;                                  // return the object                 
7 }
View Code

  The createAnother() function can be used in the following way:

1 var person = {
2     name:  "Nicholas",
3     friends:  ["Shelby", "Court", "Van"]
4 };
5 
6 var anotherPerson = createAnother(person);
7 anotherPerson.sayHi();                 // "hi"
View Code

  The code in this example returns a new object based on person. The anotherPerson object has all of the properties and methods of person but adds a new method called sayHi().

  Parasitic inheritance is another pattern to use when you are concerned primarily with objects and not with custom types and constructors. Keep in mind that adding functions to objects using parasitic inheritance leads to inefficiencies related to function reuse, similar to the constructor pattern.

 

Parasitic Combination Inheritance

  The most inefficient part of the Combination pattern is that the supertype constructor is always called twice: once to create the subtype's prototype, and once inside the subtype constructor. 

  Parasitic combination inheritance uses constructor stealing to inherit properties but uses a hybrid form of prototype chaining to inherit methods. The basic idea is this: instead of calling the supertype constructor to assign the subtype's prototype, all you need is a copy of the supertype's prototype. Essentially, use parasitic inheritance to inherit from the supertype's prototype and then assign the result to the subtype's prototype. The basic pattern for parastic combination inheritance is as follows:

1 function inheritPrototype(SubType, SuperType){
2     var prototype = object(SuperType.prototype);            // create object
3     prototype.constructor = SubType;                              // augment object
4     SubType.prototype = prototype;                                 // assign object  
5 }
View Code

  A call to inheritPrototype() can replace the subtype prototype assignment in the previous example, as shown here:

 1 function SuperType(name){
 2     this.name = name;
 3     this.colors = ["red", "blue", "green"];
 4 }
 5 
 6 SuperType.prototype.sayName = function(){
 7     alert(this.name);
 8 };
 9 
10 function SubType(name, age){
11     SuperType.call(this, name);
12     this.age = age;
13 }
14 
15 inheritPrototype(SubType, SuperType);
16 
17 SubType.prototype.sayAge = function(){
18     alert(this.age);
19 };
View Code

  This example is more efficient in that the SuperType constructor is being called only one time, avoiding having unneccessary and unused properties on SubType.prototype. Furthermore, the prototype chain is kept intact, so both instanceof and isPrototypeOf() behave as they would normally. Parasitic combination inheritance is considered the most optimal inheritance paradigm for reference types.

posted @ 2015-05-08 17:39  林大勇  阅读(341)  评论(0编辑  收藏  举报