Jonathan Kemp
@jonkemp
@ Scripps Networks
https://slides.com/jonkemp/methods-of-inheritance/live
With the exception of `null` and `undefined`, the other primitive value types (number, string and boolean) have primitive wrapper objects.
The wrapper objects have some useful properties and methods, as well as a prototype.
// avoid these:
var s = new String("my string");
var n = new Number(101);
var b = new Boolean(true);
// better and simpler:
var s = "my string";
var n = 101;
var b = true;
The Object constructor is also a primitive wrapper object with static methods and a prototype.
// Examples of static methods
Object.assign()
Object.create()
Object.defineProperties()
// Examples of methods on the prototype
Object.prototype.hasOwnProperty()
Object.prototype.toString()
Object.prototype.valueOf()
For full documentation, see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
// Object contructor
var myObj = new Object();
// Object Literal
var myObj = {};
// Define a constructor and augment its prototype
var Mammal = function (name) {
this.name = name;
};
Mammal.prototype.get_name = function () {
return this.name;
};
Mammal.prototype.says = function () {
return this.saying || '';
};
var Cat = function (name) {
this.name = name;
this.saying = 'meow';
};
// and replacing Cat.prototype with a new instance of Mammal
Cat.prototype = new Mammal();
Cat.prototype.get_name = function () {
return this.says() + ' ' + this.name + ' ' + this.says();
};
var myCat = new Cat('Henrietta');
var says = myCat.says(); // Mammal.prototype
var name = myCat.get_name(); // Cat.prototype
Despite the borrowed implications of the common name "prototypal inheritance", JavaScript's mechanism works quite differently.
If we add a new property to a prototype, that property will immediately be visible in all of the objects that are based on that prototype.
Because of the dynamic nature of JavaScript’s prototypal inheritance, all values are immediately endowed with the new methods, even values that were created before the methods were created.
In programming, the idea of "inheritance" is most closely associated with the idea of "copying" from parent to child.
In classical languages, objects are instances of classes, and a class can inherit from another class.
JavaScript's constructor functions are reminiscent of the classical languages.
// Define a constructor and augment its prototype
var Mammal = function (name) {
this.name = name;
};
Mammal.prototype.get_name = function () {
return this.name;
};
Mammal.prototype.says = function () {
return this.saying || '';
};
// Make an instance
var myMammal = new Mammal('Herb the Mammal');
var name = myMammal.get_name(); // 'Herb the Mammal'
// Inherits from Mammal by defining its constructor function
var Cat = function (name) {
this.name = name;
this.saying = 'meow';
};
// and replacing Cat.prototype with a new instance of Mammal
Cat.prototype = new Mammal();
// Augment the new prototype with
// purr and get_name methods.
Cat.prototype.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
Cat.prototype.get_name = function () {
return this.says() + ' ' + this.name + ' ' + this.says();
};
var myCat = new Cat('Henrietta');
var says = myCat.says(); // 'meow'
var purr = myCat.purr(5); // 'r-r-r-r-r'
var name = myCat.get_name(); // 'meow Henrietta meow'
class Mammal {
constructor(name) {
this.name = name;
}
get_name() {
return this.name;
}
says() {
return this.saying || '';
}
}
const myMammal = new Mammal('Herb the Mammal');
const name = myMammal.get_name(); // 'Herb the Mammal'
class Cat extends Mammal {
constructor(name) {
this.name = name;
this.saying = 'meow';
}
purr() {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
}
get_name() {
return this.says() + ' ' + this.name + ' ' + this.says();
}
}
const myCat = new Cat('Henrietta');
const says = myCat.says(); // 'meow'
const purr = myCat.purr(5); // 'r-r-r-r-r'
const name = myCat.get_name(); // 'meow Henrietta meow'
In contrast to most other languages, JS is somewhat unique that you can actually create objects directly without the notion of classes or other abstractions.
Prototypal inheritance is powerfully expressive, but is not widely understood.
In a purely prototypal pattern, we dispense with classes (or class-like syntax).
We focus instead on the objects.
Prototypal inheritance is conceptually simpler than classical inheritance: a new object can inherit the properties of an old object.
// object literal
var myMammal = {
name: 'Herb the Mammal',
get_name : function () {
return this.name;
},
says: function () {
return this.saying || '';
}
};
// Object.create method
var myCat = Object.create(myMammal);
// customize the new instances
myCat.name = 'Henrietta';
myCat.saying = 'meow';
myCat.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
myCat.get_name = function () {
return this.says() + ' ' + this.name + ' ' + this.says();
};
var says = myCat.says(); // 'meow'
var purr = myCat.purr(5); // 'r-r-r-r-r'
var name = myCat.get_name(); // 'meow Henrietta meow'
Instead of calling function constructors like new Bar(), we use Object.create().
Added in ES5, a method which allows us to create a new object and optionally provide another object to prototype link it to.
A closure is the combination of a function bundled together with references to it's surrounding state.
A closure gives you access to an outer function's vars and parameters from an inner function, even after the outer function has returned.
Among other things, closures are commonly used to give objects data privacy.
// Module pattern
var mammal = function() {
var name = 'Herb the Mammal';
return {
getName: function() {
return name;
},
says: function() {
return saying || '';
}
};
}());
// Revealing module pattern
var mammal = function() {
var name = 'Herb the Mammal';
function getName() {
return name;
}
function says() {
return saying || '';
}
return {
getName: getName,
says: says
};
}());
In JavaScript, any function can create new objects. When it’s not a constructor function or class, it’s called a factory function
var vehiclePrototype = {
doors: 4,
state: 'brand new',
color: 'silver'
};
var carOptions = {
color: 'yellow'
};
var truckOptions = {
wheelSize: 'large',
state: 'used',
color: 'blue'
};
// Our Factory method for creating new Vehicle instances
function createVehicle(options) {
return _.extend(Object.create(vehiclePrototype), options);
}
// Create an instance of our factory that makes cars
var car = createVehicle(carOptions);
console.log(car.color); // 'yellow'
console.log(car.doors); // 4
console.log(car.state); // 'brand new'
// Create an instance of our factory that makes trucks
var truck = createVehicle(truckOptions);
console.log(truck.color); // 'blue'
console.log(truck.wheelSize); // 'large'
console.log(truck.state); // 'used'
Functional inheritance makes use of a factory function, and then tacks on new properties using concatenative inheritance.
var mammal = function (spec) {
var that = {};
that.getName: function() {
return spec.name;
};
that.says: function() {
return spec.saying || '';
};
return that;
};
var myMammal = mammal({name: 'Herb'});
var cat = function (spec) {
spec.saying = spec.saying || 'meow';
var that = mammal(spec);
that.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
that.getName = function() {
return that.says() + ' ' + spec.name + ' ' + that.says();
},
return that;
};
var myCat = cat({name: 'Henrietta'});
var says = myCat.says(); // 'meow'
var purr = myCat.purr(5); // 'r-r-r-r-r'
var name = myCat.getName(); // 'meow Henrietta meow'
Concatenative inheritance is the process of copying the properties from one object to another, without retaining a reference between the two objects.
// object literal
var myMammal = {
name: 'Herb the Mammal',
get_name : function () {
return this.name;
},
says: function () {
return this.saying || '';
}
};
var myCat = _.extend(Object.create(myMammal), {
name: 'Henrietta',
saying: 'meow',
purr: function(n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
},
get_name = function() {
return this.says() + ' ' + this.name + ' ' + this.says();
}
});
var says = myCat.says(); // 'meow'
var purr = myCat.purr(5); // 'r-r-r-r-r'
var name = myCat.get_name(); // 'meow Henrietta meow'
A proposal for defining modules in which both the module and dependencies can be asynchronously loaded.
Unfortunately none of the major JavaScript runtimes support ES2015 modules in their current stable branches. This means no support in Firefox, Chrome or Node.js.
https://slides.com/jonkemp/methods-of-inheritance