2018 Edition
Why Classical / Prototypical?
var Animal = function() {};
var cat = new Animal();
cat instanceof Animal; // true
Utilizes all the language features provided for inheritance
Why Classical / Prototypical?
class Animal {}
var cat = new Animal();
cat instanceof Animal; // true
ES6 "class" keyword is prototypical inheritance
Why Classical / Prototypical?
var util = requite('util');
// Parent Ctor
var Animal = function() {};
var Cat = function() {
Animal.call(this);
};
util.inherits(Cat, Animal);
For ES5 Node.js Provides helpers built in the language (versions 6 and bellow)
Why Classical / Prototypical?
Alternatives to Classical Inheritance
Why Classical / Prototypical?
Closing points onto WHY
// Parent Ctor
class AnimalES6 {
constructor(name) {
this.name = name;
}
}
class Cat extends AnimalES6 {
constructor(color) {
super('cat'); //call the parent method with super
this.color = color;
}
}
ES6 and beyond
var util = require('util');
// Parent Ctor
var Animal = function() {};
var Cat = function() {
Animal.call(this);
};
util.inherits(Cat, Animal);
ES5 in Node.js
var inherits = function(ChildCtor, ParentCtor) {
ChildCtor.prototype = Object.create(ParentCtor.prototype);
ChildCtor.prototype.constructor = ParentCtor;
};
// Parent Ctor
var Animal = function() {};
var Cat = function() {
Animal.call(this);
};
inherits(Cat, Animal);
How "util.inherits()" works
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
ChildCtor.prototype.constructor = ChildCtor;
};
// Parent Ctor
var Animal = function() {};
var Cat = function() {
Animal.call(this);
};
inherits(Cat, Animal);
Vanilla Javascript ES3
Javascript is a prototypical language
var str = new String();
var bool = new Boolean();
var num = new Number();
The Prototype is the blueprint for creating objects and instances
var Animal = function(name) {
this.name = name;
};
Animal.prototype.getName = function() {
return this.name;
};
var pony = new Animal('pony');
pony.getName(); // "pony"
The instance
Invokes the Constructor
The Constructor
Instance local variable
A method
Going down the rabbit hole...
var fn = function() {}
fn.prototype
// fn {}
// -> constructor: function()
// -> __proto__: Object
All prototypes have at least two properties:
Going downer the rabbit hole...
var str = 'a string';
str.__proto__;
// Outputs the Object that was used to construct
// the "str" instance:
// String {length: 0, [[PrimitiveValue]]: ""}
All variables of any type in Javascript have the __proto__ property!
All variables of any type in Javascript have the __proto__ property!
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
ChildCtor.prototype.constructor = ChildCtor;
};
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
};
ChildCtor.prototype.constructor = ChildCtor;
Create a temporary Constructor to copy the Parent Prototype on.
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
};
ChildCtor.prototype.constructor = ChildCtor;
Copy the Parent's Prototype to the temporary Constructor's prototype
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
};
ChildCtor.prototype.constructor = ChildCtor;
Instanciate the temporary Ctor and assign it by overwriting the Child's prototype.
This is where inheritance happens.
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
};
ChildCtor.prototype.constructor = ChildCtor;
Let's break this out, too much happened here:
"var inst = new TempCtor()" we created an instance
The instance has a __proto__ referencing the TempCtor
Which in our case is the Parent's Prototype.
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
};
ChildCtor.prototype.constructor = ChildCtor;
We overwrite the prototype's special property "constructor"
Using the actual Child Ctor
Remember, the Ctor is a function
var inherits = function(ChildCtor, ParentCtor) {
function TempCtor() {};
TempCtor.prototype = ParentCtor.prototype;
ChildCtor.prototype = new TempCtor();
};
ChildCtor.prototype.constructor = ChildCtor;
So essentially we discard the default Child's prototype
Overwriting it with the instance of the Parent Ctor
var inherits = function(ChildCtor, ParentCtor) {
ClildCtor.prototype = ParentCtor.prototype;
};
This isn't a "copy", this is "assignment by reference"
Every change you perform on the Child's prototype will directly change the Parent's prototype, they are the same
Why not:
var inherits = function(ChildCtor, ParentCtor) {
ClildCtor.prototype = new ParentCtor()
};
This is a "copy"
BUT you are also invoking the Parent's Ctor which can have severe side-effects
Why not:
We copied the Parent's prototype to the Child...
... now we need to make sure that all the Constructors from our Parents will be invoked!
... in the right Scope!
var util = require('util');
var Animal = function(name) {
this.name = name;
};
Animal.prototype.getName = function() {
return this.name;
};
// Inherits from Animal
var Cat = function(name, color) {
Animal.call(this, name);
this.color = color;
};
util.inherits(Cat, Animal);
Cat.prototype.getColor = function() {
return this.color;
};
That's the key Part right there!
99% of your problems will come from Scope
Scope refers to where variables and functions are accessible, and in what context it is being executed.
In the case of an Instance, scope is critical in maintaining access to methods and local properties
Base Definition
var Animal = function(name) {
this.name = name;
};
Animal.prototype.getName = function() {
return this.name;
};
Retains Scope - Vanilla
var animal = new Animal('cat');
animal.getName();
// "cat"
Looses Scope
var animal = new Animal('cat');
var getName = animal.getName;
getName();
// undefined
Retains Scope - bind
var animal = new Animal('cat');
var getName = animal.getName.bind(animal);
getName();
// "cat"
Retains Scope - call
var animal = new Animal('cat');
var getName = animal.getName;
getName.call(animal);
// "cat"
Retains Scope - apply
var animal = new Animal('cat');
var getName = animal.getName;
getName.apply(animal);
// "cat"
In Practice...
var Animal = function(name) {
this.name = name;
// this will fail
this.on('some event', this.doSomething);
// this is ok
this.on('some event', this.doSomething.bind(this));
};
var animal = new Animal('cat');
// this will fail
onEvent(animal.onEventHandler);
// this is ok
onEvent(animal.onEventHandler.bind(animal));
// With Arrow function
onEvent(() => animal.onEventHandler);
// Inherits from Animal
var Cat = function(name, color) {
Animal.call(this, name);
this.color = color;
};
util.inherits(Cat, Animal);
Cat.prototype.getColor = function() {
return this.color;
};
1
3
2
// Inherits from Animal
Class Cat extends Animal {
constructor (name, color) {
super(name);
this.color = color;
}
getColor() {
return this.color;
}
}
1
2
From Node 8
var Animal = function(name) {
this.name = name;
};
// Never do this
Animal.prototype.name = '';
var util = require('util');
var Animal = function(name) {
this.name = name;
};
// Inherits from Animal
var Cat = function(name, color) {
Animal.call(this, name);
this.color = color;
};
Cat.prototype.getColor = function() {
return this.color;
};
// Will overwrite and DELETE getColor()
util.inherits(Cat, Animal);
Scope on callbacks
Animal.prototype.getName = function(cb) {
this.db.getName(function(name) {
// NEW SCOPE HERE, "this" REFERS TO
// THIS FUNCTION'S CONTEXT
cb(name);
});
};
var Animal = function() {}
var Animal = function() {
this.name = null;
this.color = null;
}
var Animal = function() {
this.name = null;
this.color = null;
}
Animal.staticFn = function() {
// Will not be inherited
};
Thank you
Questions