Slide 1

Slide 1 text

Classical Inheritance in Javascript 2018 Edition

Slide 2

Slide 2 text

Who is Thanasis Professional Community Open Source Eng Mgr at Waldo CTO at QallOut CTO at Insight Replay Founder Over 40 NPM packages Contributor to major Node.js packages Avid OSS author Local Node.js Meetup organizer (Greece) founder organizer skgtech.io DEVit Conference Software Engineer, CTO, founder Recently moved to London from Greece Available for hire

Slide 3

Slide 3 text

Inheritance in Javascript Lots of ways It's complicated We'll talk about Classical / Prototypical

Slide 4

Slide 4 text

Why Classical / Prototypical?

Slide 5

Slide 5 text

Why Classical / Prototypical? var Animal = function() {}; var cat = new Animal(); cat instanceof Animal; // true Forces use of a Constructor Uses the "new" keyword "instanceof" works Utilizes all the language features provided for inheritance

Slide 6

Slide 6 text

Why Classical / Prototypical? class Animal {} var cat = new Animal(); cat instanceof Animal; // true ES6 "class" keyword is prototypical inheritance

Slide 7

Slide 7 text

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)

Slide 8

Slide 8 text

Why Classical / Prototypical? Alternatives to Classical Inheritance A bunch of variations on how to do Classical (Object.create, Object.assign) Composition Object literals and the rest...

Slide 9

Slide 9 text

Why Classical / Prototypical? Closing points onto WHY It is the language's intended way for inheritance. Generally accepted as the way to do inheritance in Javascript. Signal / Noise ratio in JS land is a problem, stay confident & focused.

Slide 10

Slide 10 text

How to Inherit // 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

Slide 11

Slide 11 text

How to Inherit var util = require('util'); // Parent Ctor var Animal = function() {}; var Cat = function() { Animal.call(this); }; util.inherits(Cat, Animal); ES5 in Node.js

Slide 12

Slide 12 text

How to Inherit 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

Slide 13

Slide 13 text

How to Inherit 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

Slide 14

Slide 14 text

What did just happen? ... lets talk about the prototype

Slide 15

Slide 15 text

The Javascript Prototype Javascript is a prototypical language All Objects have a prototype Everything in JS is an Object The Prototype is the blueprint for creating objects and instances The Instances or Objects do not have a prototype Functions have a prototype var str = new String(); var bool = new Boolean(); var num = new Number();

Slide 16

Slide 16 text

The Javascript Prototype 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

Slide 17

Slide 17 text

The Javascript Prototype Going down the rabbit hole... var fn = function() {} fn.prototype // fn {} // -> constructor: function() // -> __proto__: Object All prototypes have at least two properties: constructor: A Function that gets invoked when constructing the instance __proto__: A reference to the parent prototype

Slide 18

Slide 18 text

The Javascript Prototype 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!

Slide 19

Slide 19 text

The Javascript Prototype All variables of any type in Javascript have the __proto__ property! Remember: __proto__ is a reference Points to the Object that constructed the instance Instances using the "new" keyword have a __proto__ that points to the Ctor

Slide 20

Slide 20 text

Inheritance Explained var inherits = function(ChildCtor, ParentCtor) { function TempCtor() {}; TempCtor.prototype = ParentCtor.prototype; ChildCtor.prototype = new TempCtor(); ChildCtor.prototype.constructor = ChildCtor; };

Slide 21

Slide 21 text

Inheritance Explained 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.

Slide 22

Slide 22 text

Inheritance Explained 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

Slide 23

Slide 23 text

Inheritance Explained 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.

Slide 24

Slide 24 text

Inheritance Explained 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.

Slide 25

Slide 25 text

Inheritance Explained 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

Slide 26

Slide 26 text

Inheritance Explained 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

Slide 27

Slide 27 text

Inheritance Explained 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:

Slide 28

Slide 28 text

Inheritance Explained 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:

Slide 29

Slide 29 text

We are not finished yet!

Slide 30

Slide 30 text

The Inheritance So... 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!

Slide 31

Slide 31 text

The Inheritance The Scope....

Slide 32

Slide 32 text

The Inheritance 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!

Slide 33

Slide 33 text

The Scope 99% of your problems will come from Scope SO PAY ATTENTION

Slide 34

Slide 34 text

The 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

Slide 35

Slide 35 text

The Scope Base Definition var Animal = function(name) { this.name = name; }; Animal.prototype.getName = function() { return this.name; };

Slide 36

Slide 36 text

The Scope 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"

Slide 37

Slide 37 text

The Scope 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"

Slide 38

Slide 38 text

The Scope 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);

Slide 39

Slide 39 text

So, back to inheritance

Slide 40

Slide 40 text

Inheritance Ctor // 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. First thing you do in your Ctor is to call your Parent's Ctor using your own, new, context (scope) 2. Then, right after the Ctor, invoke the inherits function 3. Afterwards define your methods. Can even overwrite the parent's 1 3 2

Slide 41

Slide 41 text

Inheritance Ctor // Inherits from Animal Class Cat extends Animal { constructor (name, color) { super(name); this.color = color; } getColor() { return this.color; } } 1. First thing you do in your Ctor is to call your Parent's Ctor with super 2. Afterwards define your methods. Can even overwrite the parent's 1 2 From Node 8

Slide 42

Slide 42 text

The Prototype Chain

Slide 43

Slide 43 text

The Prototype Chain The more you extend a Ctor creating childs the longer the chain gets Js will lookup serially for a method all the way up to the last prototype This can result in performance hits in some particular cases

Slide 44

Slide 44 text

Common Gotchas Only define methods on the prototype Everything else on the Ctor var Animal = function(name) { this.name = name; }; // Never do this Animal.prototype.name = '';

Slide 45

Slide 45 text

Common Gotchas Never invoke the inheritance method after your methods 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);

Slide 46

Slide 46 text

Common Gotchas 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); }); }; Use .bind(this) at end of the anonymous function expression used as a callback Use the Arrow Function Expression ( fat arrow => )

Slide 47

Slide 47 text

Trivia & Best Practises We call Constructors "Constructors" and not "Classes" because they do not behave like Classes They resemble a Class, thus the term "Classical Inheritance" But in reality, this is "Prototypical Inheritance" We signify a Ctor by capitalizing the first letter var Animal = function() {}

Slide 48

Slide 48 text

Trivia & Best Practises Use the constructor strictly to construct the instance Asynchronous operations in Ctors are forbidden! Define any and all properties in the constructor Use null to initialize properties with no value Never return any value from the Ctor It will screw up everything var Animal = function() { this.name = null; this.color = null; }

Slide 49

Slide 49 text

Trivia & Best Practises You may define a method on the Ctor directly, that is considered a Static function and will not be inherited var Animal = function() { this.name = null; this.color = null; } Animal.staticFn = function() { // Will not be inherited };

Slide 50

Slide 50 text

Thank you Thanasis Polychronakis @thanpolas https://speakerdeck.com/thanpolas

Slide 51

Slide 51 text

Questions Thanasis Polychronakis @thanpolas https://speakerdeck.com/thanpolas ¿