MozillaZine

Differential inheritance in JavaScript

From MozillaZine Knowledge Base

(Difference between revisions)
Revision as of 17:03, 6 March 2005
Asqueella (Talk | contribs)
(Words of caution - formatting)
<-- Previous diff
Revision as of 17:06, 6 March 2005
Asqueella (Talk | contribs)
(Words of caution - more formatting)
Next diff -->
Line 49: Line 49:
*Internet Explorer currently does not support the <code>__proto__</code> property. It is recommended that this technique be reserved for situations where the developer can be certain that the script will be executed by Mozilla or other interpreters that support the <code>__proto__</code> property, like Safari and KJS. *Internet Explorer currently does not support the <code>__proto__</code> property. It is recommended that this technique be reserved for situations where the developer can be certain that the script will be executed by Mozilla or other interpreters that support the <code>__proto__</code> property, like Safari and KJS.
-*Also note that when you change the Object prototype, <i>all</i> javascript objects are affected. While this is what you wanted, there is a gotcha when it comes to object property enumeration&mdash;the <code>for( variable in object )</code> iterator will also see your inherited function, and any code using the basic object as an associative array and iterating it like this without safeguarding from inherited properties (Object has no inherited enumerable properties by default) will most likely break. If your code lives in a vacuum where it will never affect code invented elsewhere and you still want to use both of these techniques, you can rewrite this iteration to avoid inherited properties using the <code>__proto__</code> property mentioned in the first gotcha above:+*Also note that when you change the <code>Object</code> prototype, <i>all</i> javascript objects are affected. While this is what you wanted, there is a gotcha: when it comes to object property enumeration, the <code>for( variable in object )</code> iterator will also see your inherited function, and any code using the basic object as an associative array and iterating it like this without safeguarding from inherited properties (<code>Object</code> has no inherited enumerable properties by default) will most likely break. If your code lives in a vacuum where it will never affect code invented elsewhere and you still want to use both of these techniques, you can rewrite this iteration to avoid inherited properties using the <code>__proto__</code> property mentioned in the first gotcha above:
<pre> <pre>

Revision as of 17:06, 6 March 2005

Differential inheritance in Javascript

Netscape 4.x and Mozilla both provide access to the internal prototype information associated with an Object in Javascript using the __proto__ property. By manipulating this property, a developer can emulate "differential inheritance", a common technique in prototype-oriented programming models.

Differential Inheritance is a common prototype-oriented model that uses the concept that most objects are derived from other, more generic objects, and only differ in a few small aspects. Each object maintains a reference to its prototype and a table of properties that are different. The following code provides a simple method for "cloning" an object and a fundamental object, since the global variable Object actually refers to the Object constructor, not an actual Object.

Object.prototype.clone = function(){
  var newObject = new this.constructor();
  newObject.__proto__ = this;
  return newObject;
};

Root = new Object();

Using "clone", it becomes possible to simply derive more specific objects from a generic prototype. The following is a simple example of building up increasingly more specific objects using the clone method and differential inheritance.

Record = Root.clone();
Record.toString = function(){ return "a Record"; };
 
Person = Root.clone();
Person.firstName = false;
Person.lastName = false;
Person.toString = function(){ 
    if( this.firstName ){
      if( this.lastName ){
        return this.firstName + " " +this.lastName; 
      }else{
        return this.firstName;
      }
    }else{
      if( this.lastName ){
        return this.lastName;
      }else{
        return "a Person";
    }
  }
}
 
JoePerson = Person.clone();
JoePerson.firstName = "Joe";
alert( JoePerson.toString() );

Words of caution

  • Internet Explorer currently does not support the __proto__ property. It is recommended that this technique be reserved for situations where the developer can be certain that the script will be executed by Mozilla or other interpreters that support the __proto__ property, like Safari and KJS.
  • Also note that when you change the Object prototype, all javascript objects are affected. While this is what you wanted, there is a gotcha: when it comes to object property enumeration, the for( variable in object ) iterator will also see your inherited function, and any code using the basic object as an associative array and iterating it like this without safeguarding from inherited properties (Object has no inherited enumerable properties by default) will most likely break. If your code lives in a vacuum where it will never affect code invented elsewhere and you still want to use both of these techniques, you can rewrite this iteration to avoid inherited properties using the __proto__ property mentioned in the first gotcha above:
for( variable in object )
  if( object.__proto__[variable] !== object[variable] )
  { /* your loop body here instead */ }