Prototypal Inheritance
Steve Venzerul
Good Ol' Classical Inheritance
//Fictional language (probably C++)
class Animal {
public:
Animal();
void walk();
void makeNoise();
void eat();
void poop();
};
class Duck : public Animal {
public:
Duck() : Animal();
void quack();
}
Object
Object
Duck duffy = new Duck();
duffy.quack();
Instance
Proto-not-so-typical
Diagram Source: http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
Functions instead of Objects
Function
function Person() {
this.name = 'Anon';
}
Function
function Manager(name) {
Person.call(this);
this.name = name;
}
Manager.prototype.askYouToComeInOnSatureday = function() {
Person.prototype.sayHello.call(this);
var msg = "Yeeeeeeeeah, I\'m gonna need" +
"you to go ahead and come in on Satureday.";
console.log(msg);
};
Instance
var EvilManager = new Manager('Lumberg');
EvilManager.askYouToComeInOnSatureday()
Not exactly inheritance
function Person() {
this.name = 'Anonymous';
}
Person.prototype.sayHello = function() {
console.log('Howdy, my name\'s', this.name);
};
function Man() {
Person.call(this);
this.name = 'Timmy';
}
Man.prototype = new Person();
//Alternative: Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man;
var t = new Man();
t.sayHello();
//Howdy, my name's Timmy
console.log(t instanceof Man);
//true
console.log(t instanceof Person);
//true
//Delegation
console.log(Object.getPrototypeOf(t) === Man.prototype);
A helping hand
//ES5 Inheritance helper.
//Avoids invoking the parent constructor function.
function inherits(child, parent) {
var Temp = function(){};
Temp.prototype = parent.prototype;
child.prototype = new Temp();
child.prototype.constructor = child;
}
//Greatly simplified in ES6
function inherits(child, parent) {
Object.setPrototypeOf(child.prototype, parent.prototype);
}
Something better
Why are we messing with the constructor?
//ES5 Inheritance helper.
//Avoids invoking the parent constructor function.
function inherits(child, parent) {
var Temp = function(){};
Temp.prototype = parent.prototype;
child.prototype = new Temp();
//---------------------------
child.prototype.constructor = child;
//---------------------------
}
The thing about `instanceof`
//JS's instanceof operator is kinda dumb. It can be easily
//fooled by futzing about with the prototype chain.
//To make sure everything is correct we fix the constructor.
child.prototype.constructor = child;
//Without that line the following will yield false
Child instanceof Parent
//false <-- see, I told you.
Statically speaking
function ICanHazStatics() {}
ICanHazStatics.giveCheese = function() {
console.log('Got cheese!');
};
ICanHazStatics.eatCheese = function() {
console.log('nom nom nom');
};
//Not to be confused with prototype methods
var chaz = new ICanHazStatics();
chaz.giveCheese();
//TypeError: giveCheese is not a function
ICanHazStatics.giveCheese();
//Got cheese!
The magic of Object.create()
function SuperCar() {}
function Koenigsegg() {
SuperCar.call(this);
}
Koenigsegg.prototype = Object.create(SuperCar.prototype);
Koenigsegg.prototype.constructor = Koenigsegg;
When the curtain falls
//Object.create shim, courtesy of the Crockford.
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
//Object.create ES6-sty
Object.create = function(proto) {
var obj = {};
Object.setPrototypeOf(obj, proto);
return obj;
};
__proto__ - The hidden treasure
Diagram Source: http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
Hands off the __proto__
function Cheese() {
this.color = 'Yellow';
}
Cheese.prototype.isSmelly = function() {
console.log('Oh god yes!');
};
function Cheddar() {
this.color = 'Orange-y';
}
Cheddar.prototype = Object.create(Cheese.prototype);
Cheddar.prototype.constructor = Cheddar;
var slice = new Cheddar();
//Some truths
slice.__proto__ === Cheddar.prototype;
//true
Cheddar.prototype.__proto__ === Cheese.prototype;
//true
__proto__ is not standard. Except, ES2015 made it standard for backwards compatibility because all current major browsers support and they didn't want to 'break the web'. However, they _strongly_ discourage the public from using this property. What they're saying is -- here's a gun, but don't shoot anyone.
//ES2015 introduces Object.getPrototypeOf() and Object.setPrototypeOf()
var slice = new Cheddar();
Object.getPrototypeOf(slice) === Cheddar.prototype;
var slice = {};
Object.setPrototypeOf(slice, Cheddar.prototype);
slice.isSmelly();
//Oh god yes!
console.log(slice.color);
//undefined
//Woah, woah, woah! Why is the color undefined?
//Because the constructor function Cheddar() was never
//run on the new slice object we just created.
The path of the righteous
Inheritance sugar in your Java-script
//ES6
class PartyAnimal {
constructor(name) {
this.name = name;
}
makeNoise(moreNoise) {
console.log('Paaaaartttyyyy!,', moreNoise);
}
}
class SpikyHairedGentleman extends PartyAnimal {
constructor(name, lifts) {
super(name);
this.lifts = lifts;
}
makeNoise() {
super.makeNoise('Where\'s the vodka at bro?');
}
doesLift() {
if (this.lifts) {
console.log('Of course bro, never skip leg day.');
} else {
console.log('Planning on it bro!');
}
}
}
let Fabio = new SpikyHairedGentleman('Fabio', true);
Fabio.makeNoise();
//Paaaaartttyyyy!, Where's the vodka at bro?
Fabio.doesLift();
//Of course bro, never skip leg day.
ES5, leading cause of Repetitive Strain Injury
function ParentConstructor() {}
function SomeConstructor() {
ParentConstructor.call(this);
}
SomeConstructor.prototype.method = function() {
SuperClass.prototype.method.apply(this, args);
}
Doctor ES2015
class SomeConstructor extends ParentConstructor {
//Not needed. Done automatically for you by the engine.
constructor(...args) {
super(...args);
}
inheritedMethod() {
super.inheritedMethod();
}
}
//But Steve, what happens if I call `sm.inheritedMethod.call(this)`?
//The answer is Zalgo, of course.
//In ES6 methods that contain super() references have something called a
//HomeObject which essentially binds them to the object they were declared
//in. This effectively means you can't .call() or .apply() them. Well, you
//can, but you won't have the 'this' you're expecting, leading to serious
//and persistent programmer tears.
(new SomeConstrctor()).inheritedMethod.call(this); //No bueno
Subclassing Native Types
class ShinyThings extends Array {
getShiny() {
return this.filter(thing => {
return thing.shines;
});
}
}
let a = new ShinyThings({shines: true}, {shines: false}, {shines: true}, {shines: true});
console.log(a.getShiny());
//[{shines: true}, {shines: true}, {shines: true}]
console.log(a.length);
//4
class UniqueObject extends Object {
constructor(...args) {
super(...args);
this.id = this._getUnique();
}
_getUnique(len = 16) {
let set = 'abcdefghijklmnopqestuvwxyz1234567890',
id = '';
for(let i = 0; i < len; i++) {
id += set[Math.floor(Math.random() * len)];
}
return id;
}
}
let ub = new UniqueObject();
console.log(ub);
//UniqueObject { id: '1f8v9h24v8h2358' }
Snowflake Object
Subclassing String - Attack of the V8 Bug
//Wanna subclass the String object in an easy and fun way? NO SOUP FOR YOU!
class Str extends String {
capitalize() {
return `${this.slice(0, 1).toUpperCase()}${this.slice(1)}`;
}
}
s.capitalize()
//TypeError: s.capitalize is not a function
class Str extends String {
constructor(...args) {
super(...args);
Object.setPrototypeOf(this, new.target.prototype);
}
capitalize() {
return `${this.slice(0, 1).toUpperCase()}${this.slice(1)}`;
}
}
var s = new Str('yarn');
console.log(s.constructor);
//[Function: Str]
console.log(Object.getPrototypeOf(s));
//Str {}
console.log(s.capitalize());
//Yarn
setPrototypeOf() saving the day
Static Recall
class UniqueObject extends Object {
constructor() {
super();
this.id = UniqueObject._getUnique();
}
static _getUnique(len = 16) {
let set = 'abcdefghijklmnopqestuvwxyz1234567890',
id = '';
for(let i = 0; i < len; i++) {
id += set[Math.floor(Math.random() * len)];
}
return id;
}
}
let ub = new UniqueObject();
console.log(ub);
//UniqueObject { id: '3jfu6kb8tud7e9r5' }
console.log(UniqueObject._getUnique());
//9fkv85m90d8j3y47
//Equivalent ES5
UniqueObject.getUnique = function() {
//yada yada
}
Questions?
Prototypal Inheritance
By signupskm
Prototypal Inheritance
- 1,527