Good Example of JavaScript's Prototype-Based Inheritance

JavascriptPrototypal Inheritance

Javascript Problem Overview


I have been programming with OOP languages for over 10 years but I'm learning JavaScript now and it's the first time I've encountered prototype-based inheritance. I tend to learn fastest by studying good code. What's a well-written example of a JavaScript application (or library) that properly uses prototypal inheritance? And can you describe (briefly) how/where prototypal inheritance is used, so I know where to start reading?

Javascript Solutions


Solution 1 - Javascript

As mentioned, the movies by Douglas Crockford give a good explanation about the why and it covers the how. But to put it in a couple of lines of JavaScript:

// Declaring our Animal object
var Animal = function () {

    this.name = 'unknown';

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

    return this;
};

// Declaring our Dog object
var Dog = function () {

    // A private variable here        
    var private = 42;

    // overriding the name
    this.name = "Bello";

    // Implementing ".bark()"
    this.bark = function () {
        return 'MEOW';
    }  

    return this;
};


// Dog extends animal
Dog.prototype = new Animal();

// -- Done declaring --

// Creating an instance of Dog.
var dog = new Dog();

// Proving our case
console.log(
    "Is dog an instance of Dog? ", dog instanceof Dog, "\n",
    "Is dog an instance of Animal? ", dog instanceof Animal, "\n",
    dog.bark() +"\n", // Should be: "MEOW"
    dog.getName() +"\n", // Should be: "Bello"
    dog.private +"\n" // Should be: 'undefined'
);

The problem with this approach however, is that it will re-create the object every time you create one. Another approach is to declare your objects on the prototype stack, like so:

// Defining test one, prototypal
var testOne = function () {};
testOne.prototype = (function () {
    var me = {}, privateVariable = 42;
    me.someMethod = function () {
        return privateVariable;
    };
    
    me.publicVariable = "foo bar";
    me.anotherMethod = function () {
        return this.publicVariable;
    };

    return me;

}());


// Defining test two, function
var testTwo = ​function() {
    var me = {}, privateVariable = 42;
    me.someMethod = function () {
        return privateVariable;
    };
    
    me.publicVariable = "foo bar";
    me.anotherMethod = function () {
        return this.publicVariable;
    };

    return me;
};


// Proving that both techniques are functionally identical
var resultTestOne = new testOne(),
    resultTestTwo = new testTwo();

console.log(
    resultTestOne.someMethod(), // Should print 42
    resultTestOne.publicVariable // Should print "foo bar"
);

console.log(
    resultTestTwo.someMethod(), // Should print 42
    resultTestTwo.publicVariable // Should print "foo bar"
);



// Performance benchmark start
var stop, start, loopCount = 1000000;

// Running testOne
start = (new Date()).getTime(); 
for (var i = loopCount; i>0; i--) {
    new testOne();
}
stop = (new Date()).getTime();

console.log('Test one took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');



// Running testTwo
start = (new Date()).getTime(); 
for (var i = loopCount; i>0; i--) {
    new testTwo();
}
stop = (new Date()).getTime();

console.log('Test two took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');

There is a slight downside when it comes to introspection. Dumping testOne, will result in less useful information. Also the private property "privateVariable" in "testOne" is shared in all instances, als helpfully mentioned in the replies by shesek.

Solution 2 - Javascript

Douglas Crockford has a nice page on JavaScript Prototypal Inheritance:

>Five years ago I wrote Classical Inheritance in JavaScript. It showed that JavaScript is a class-free, prototypal language, and that it has sufficient expressive power to simulate a classical system. My programming style has evolved since then, as any good programmer's should. I have learned to fully embrace prototypalism, and have liberated myself from the confines of the classical model.

Dean Edward's Base.js, Mootools's Class or John Resig's Simple Inheritance works are ways to do classical inheritance in JavaScript.

Solution 3 - Javascript

function Shape(x, y) {
    this.x = x;
    this.y = y;
}

// 1. Explicitly call base (Shape) constructor from subclass (Circle) constructor passing this as the explicit receiver
function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r = r;
}

// 2. Use Object.create to construct the subclass prototype object to avoid calling the base constructor
Circle.prototype = Object.create(Shape.prototype);

Solution 4 - Javascript

I would take a look at YUI, and at Dean Edward's Base library: http://dean.edwards.name/weblog/2006/03/base/

For YUI you can take a quick look at the lang module, esp. the YAHOO.lang.extend method. And then, you can browse the source of some widgets or utilities and see how they use that method.

Solution 5 - Javascript

There's also Microsoft's ASP.NET Ajax library, http://www.asp.net/ajax/.

There are a lot of good MSDN articles around as well, including Create Advanced Web Applications With Object-Oriented Techniques.

Solution 6 - Javascript

This is the clearest example I've found, from Mixu's Node book (http://book.mixu.net/node/ch6.html):

> I favor composition over inheritance: > > Composition - Functionality of an object is made up of an aggregate of > different classes by containing instances of other objects. > Inheritance - Functionality of an object is made up of it's own > functionality plus functionality from its parent classes. If you must > have inheritance, use plain old JS > > If you must implement inheritance, at least avoid using yet another > nonstandard implementation / magic function. Here is how you can > implement a reasonable facsimile of inheritance in pure ES3 (as long > as you follow the rule of never defining properties on prototypes): > > function Animal(name) { > this.name = name; > }; > Animal.prototype.move = function(meters) { > console.log(this.name+" moved "+meters+"m."); > }; >
> function Snake() { > Animal.apply(this, Array.prototype.slice.call(arguments)); > }; > Snake.prototype = new Animal(); > Snake.prototype.move = function() { > console.log("Slithering..."); > Animal.prototype.move.call(this, 5); > }; >
> var sam = new Snake("Sammy the Python"); > sam.move(); > > > This is not the same thing as classical inheritance - but it is > standard, understandable Javascript and has the functionality that > people mostly seek: chainable constructors and the ability to call > methods of the superclass.

Solution 7 - Javascript

ES6 class and extends

ES6 class and extends are just syntax sugar for previously possible prototype chain manipulation, and so arguably the most canonical setup.

First learn more about the prototype chain and . property lookup at: https://stackoverflow.com/a/23877420/895245

Now let's deconstruct what happens:

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}

// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3

// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype

// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc

// Class variables
// No ES6 syntax sugar apparently:
// https://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

Simplified diagram without all predefined objects:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype

Solution 8 - Javascript

I suggest looking at PrototypeJS' Class.create:
Line 83 @ http://prototypejs.org/assets/2009/8/31/prototype.js

Solution 9 - Javascript

The best examples I've seen are in Douglas Crockford's JavaScript: The Good Parts. It's definitely worth buying to help you get a balanced view on the language.

Douglas Crockford is responsible for the JSON format and works at Yahoo as a JavaScript guru.

Solution 10 - Javascript

There is a snippet JavaScript Prototype-based Inheritance with ECMAScript version specific implementations. It will automatically choose which to use between ES6, ES5 and ES3 implementations according to current runtime.

Solution 11 - Javascript

Adding an example of Prototype based inheritance in Javascript.

// Animal Class
function Animal (name, energy) {
  this.name = name;
  this.energy = energy;
}

Animal.prototype.eat = function (amount) {
  console.log(this.name, "eating. Energy level: ", this.energy);
  this.energy += amount;
  console.log(this.name, "completed eating. Energy level: ", this.energy);
}

Animal.prototype.sleep = function (length) {
  console.log(this.name, "sleeping. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "completed sleeping. Energy level: ", this.energy);
}

Animal.prototype.play = function (length) {
  console.log(this.name, " playing. Energy level: ", this.energy);
  this.energy -= length;
  console.log(this.name, "completed playing. Energy level: ", this.energy);
}

// Dog Class
function Dog (name, energy, breed) {
  Animal.call(this, name, energy);
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function () {
  console.log(this.name, "barking. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done barking. Energy level: ", this.energy);
}

Dog.prototype.showBreed = function () {
  console.log(this.name,"'s breed is ", this.breed);
}

// Cat Class
function Cat (name, energy, male) {
  Animal.call(this, name, energy);
  this.male = male;
}

Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;

Cat.prototype.meow = function () {
  console.log(this.name, "meowing. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done meowing. Energy level: ", this.energy);
}

Cat.prototype.showGender = function () {
  if (this.male) {
    console.log(this.name, "is male.");
  } else {
    console.log(this.name, "is female.");
  }
}

// Instances
const charlie = new Dog("Charlie", 10, "Labrador");
charlie.bark();
charlie.showBreed();

const penny = new Cat("Penny", 8, false);
penny.meow();
penny.showGender();

ES6 uses far easier implementation of inheritance witn the use of constructor and super keywords.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlex ReisnerView Question on Stackoverflow
Solution 1 - JavascriptDynomView Answer on Stackoverflow
Solution 2 - JavascriptGregory PakoszView Answer on Stackoverflow
Solution 3 - JavascriptVlad BezdenView Answer on Stackoverflow
Solution 4 - JavascriptRoland BoumanView Answer on Stackoverflow
Solution 5 - JavascriptKevin JonesView Answer on Stackoverflow
Solution 6 - JavascriptsupershneeView Answer on Stackoverflow
Solution 7 - JavascriptCiro Santilli Путлер Капут 六四事View Answer on Stackoverflow
Solution 8 - JavascriptTomasz DurkaView Answer on Stackoverflow
Solution 9 - JavascriptChris SView Answer on Stackoverflow
Solution 10 - JavascriptfuweichinView Answer on Stackoverflow
Solution 11 - Javascriptuser6184932View Answer on Stackoverflow