Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Classical Inheritance in Javascript

Classical Inheritance in Javascript

in this presentation we'll dive into Classical Prototypical inheritance in Javascript and node.js

Thanos Polychronakis

September 15, 2015
Tweet

More Decks by Thanos Polychronakis

Other Decks in Technology

Transcript

  1. Inheritance in Javascript Inheritance in Javascript Lots of ways It's

    complicated We'll talk about Classical / Prototypical
  2. 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
  3. Why Classical / Prototypical? var util = requite('util'); // Parent

    Ctor var Animal = function() {}; var Cat = function() { Animal.call(this); }; util.inherits(Cat, Animal); Node.js Provides helpers built in the language
  4. How to Inherit How to Inherit var util = require('util');

    // Parent Ctor var Animal = function() {}; var Cat = function() { Animal.call(this); }; util.inherits(Cat, Animal); Node.js
  5. How to Inherit 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
  6. How to Inherit 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 WTF¿??!¡¿
  7. How to Inherit 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); Vanilla Javascript ES5
  8. How to Inherit 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); Vanilla Javascript ES5 meh...
  9. How to Inherit How to Inherit // Parent Ctor class

    AnimalES6 { constructor(name) { this.name = name; } } class Cat extends AnimalES6 { constructor(color) { this.color = color; super('cat'); //call the parent method with super } } Vanilla Javascript ES6
  10. How to Inherit How to Inherit // Parent Ctor class

    AnimalES6 { constructor(name) { this.name = name; } } class Cat extends AnimalES6 { constructor(color) { this.color = color; super('cat'); //call the parent method with super } } Vanilla Javascript ES6 COOL! ... but it's sugar
  11. What did just What did just happen? happen? ... lets

    talk about the ... lets talk about the prototype prototype
  12. The Javascript Prototype 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();
  13. The Javascript Prototype 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
  14. The Javascript Prototype 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
  15. The Javascript Prototype 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!
  16. The Javascript Prototype The Javascript Prototype All variables of any

    type in Javascript have the __proto__ property! Remember! 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
  17. So WTF inheritance? So WTF inheritance? var inherits = function(ChildCtor,

    ParentCtor) { function TempCtor() {}; TempCtor.prototype = ParentCtor.prototype; ChildCtor.prototype = new TempCtor(); ChildCtor.prototype.constructor = ChildCtor; };
  18. So WTF inheritance? So WTF inheritance? 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.
  19. So WTF inheritance? So WTF inheritance? 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
  20. So WTF inheritance? So WTF inheritance? 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.
  21. So WTF inheritance? So WTF inheritance? 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.
  22. So WTF inheritance? So WTF inheritance? 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
  23. So WTF inheritance? So WTF inheritance? 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
  24. The Inheritance 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!
  25. The Inheritance The Inheritance So... 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!
  26. The Scope The Scope 99% of your problems will be

    cause of Scope SO PAY SO PAY ATTENTION ATTENTION
  27. The Scope 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
  28. The Scope The Scope Base Definition var Animal = function(name)

    { this.name = name; }; Animal.prototype.getName = function() { return this.name; };
  29. The Scope 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"
  30. The Scope 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"
  31. The Scope 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 $('body').on('load', animal.onLoad); // this is ok $('body').on('load', animal.onLoad.bind(animal));
  32. Inheritance Ctor 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 we define our own methods and can even overwrite the parent's 1 3 2
  33. The Prototype Chain 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
  34. Common Gotchas 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 = ''; Animal.prototype.getName = function() { return this.name; }; var cat = new Animal('cat'); var dog = new Animal('dog'); cat.getName(); // "dog"
  35. Common Gotchas 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);
  36. Common Gotchas Common Gotchas Scope on methods Animal.prototype.getName = function(cb)

    { this.db.getName(function(name) { // NEW SCOPE HERE, "this" REFERS TO // THIS FUNCTION'S CONTEXT cb(name); }); };
  37. Common Gotchas Common Gotchas Scope on methods Animal.prototype.getName = function(cb)

    { if (this.name) { cb(this.name); } else { var self = this; this.db.getName(function(name) { self.name = name; cb(name); }); } };
  38. Rules & Best Practises Rules & 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() {}
  39. Rules & Best Practises Rules & Best Practises Use the

    constructor strictly to construct the instance Asynchronous operations 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; }
  40. Rules & Best Practises Rules & 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 };
  41. Simple Interface Simple Interface CIP CIP var cip = require('cip');

    var Child = cip.extend(); var GrandChild = Child.extend();
  42. The Constructor The Constructor CIP CIP var cip = require('cip');

    var Animal = cip.extend(function(name) { this.name = name; }); var Cat = Animal.extend(function(color) { this.color = color; this.name = 'cat'; }); var gilly = new Cat('black'); gilly instanceof Animal; // true
  43. Singleton Pattern Singleton Pattern CIP CIP var cip = require('cip');

    var Animal = cip.extend(function(name) { this.name = name; }); var Cat = Animal.extendSingleton(function() { this.name = 'cat'; }); var theCat = Cat.getInstance(); var anotherCat = new Cat(); theCat === anotherCat; // true
  44. Mixins Mixins CIP CIP var cip = require('cip'); var Behavior

    = cip.extend(function() { this.mood = 'awesome'; }); var Animal = cip.extend(function(name) { this.name = name; }); cip.mixin(Animal, Behavior); var cat = new Animal('cat'); cat.mood; // 'awesome'
  45. Casting Casting CIP CIP // Use EventEmitter as the base

    Constructor. var EventEmitter = require('events').EventEmitter; var cip = require('cip'); var CeventEmitter = cip.cast(EventEmitter); var Thing = CeventEmitter.extend(); var newThing = new Thing(); newThing instanceof CeventEmitter; // true newThing instanceof EventEmitter; // true newThing.on('nextThing', doSomething);