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
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.
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.
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.
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
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.
I suggest looking at PrototypeJS' Class.create:
Line 83 @ http://prototypejs.org/assets/2009/8/31/prototype.js