Prototypes in Javascript

Prototypal Inheritance and the Chain Gang

First class functions?

  • Create new functions from preexisting functions at run-time
  • Store functions in collections
  • Use functions as arguments to other functions
  • Use functions as return values of other functions

A language supports first class functions if:

What is an Object

//this is an object
var myObject = {};

//you can add properties(values) and methods(functions) to an object

myObject.name = 'Sam Object';

myObject.whatIsMyName = function() {
    return this.name;
}

//That's it.

What's wrong with this Object?

I can't reuse it or make multiple copies of it.

(without call, apply, bind)

KnOW THESE WORDS

  • CLASS/FUNCTION
  • OBJECT
  • INSTANCE

...AND WHY THEY ARE DIFFERENT

ALSO IMPORTANT

  • INHERITANCE
  • CONSTRUCTOR
  • CONTEXT

What Are Prototypes

  • Prototypes are JUST Functions
    • new keyword is the difference between JUST a function and a constructor
  • Prototypes are SINGULAR
    • Prototype functions can have different CONTEXT but are same instance of function. This has a small memory footprint.
  • EVERY object has a prototype
  • Every object inherits from Object..and it's prototype
    • This is called prototypical inheritance​
  • Prototypes in JavaScript are mostly used to emulate classic OOP for creating complex objects and a means of inheritance.

Things to Know about Prototypes

  • Adding a new prototype to a class will affect ALL instances of the class even if they are already created.
  • Adding a new function to a class will affect ONLY that SINGLE instance of a class.
  • Instance members of the same name will OVERRIDE the prototype member of the same name. This happens because of order. Prototype happens first. Constructor second.
  • Prototype is a property of the constructor.
  • A function's prototype is initially a null object. Only has purpose when function is invoked as constructor with new keyword.
function Song(){} //just a function

Song.prototype.play = function() {
    return 'playing';
}

var song = Song(); //not invoked as a constructor...no prototype
console.log(song); // undefined

var realSong = new Song(); //invoked as constructor, returns reference to new Object(function)
console.log(realSong); // Object with play method available
console.log(realSong.prototype); //has no prototype, it was CREATED using Song's prototype

Using (or not) new with function invocation

function Song(){} //just a function, nothing done in constructor(call, this.*)

Song.prototype.play = function() {
    return 'playing';
}

var song = new Song(); //invoked as constructor, returns reference to new Object(function)
console.log(song); // Object with play method available

var song2 = new Song();
console.log(song2); // Object with play method available

Song.prototype.stop = function() {
  return 'stopped';
}

console.log(song, song2); //both objects now have a stop method available

Adding a function to a prototype after instance creation

Applies to all instances already created


//Constructor logic to set object properties, NOT prototype properties
function Song(){
    this.play = function() {
        return 'Hi-jacked';
    }
} 

Song.prototype.play = function() {
    return 'playing';
}

var song = new Song(); //invoked as constructor
console.log(song.play()); // 'Hi-jacked' 

//Instance method takes precedence since constructor gets called AFTER prototype

Constructors AND Prototypes

Constructor properties take precedence in order of execution.

Create instance of object:

  1. Bind prototype properties (not copy)
  2. Invoke constructor(copy)

//Constructor logic to set object properties, NOT prototype properties
function Song(){
    this.play = function() {
        throw new Error('Not implemented. Need to inherit into genre.');
    }
} 

Song.prototype.play = function() {
    return 'playing';
}

function MetalSong(){
    this.volume = 11;
}

MetalSong.prototype = Object.create(Song.prototype, {
  constructor : MetalSong
});

var reignInBlood = new MetalSong();

reignInBlood.play(); // 'playing'



Constructors AND Prototypes

Why would we do that??

The prototype Inheritance Chain

  • All objects that inherit from another object receive all prototype properties of all objects up the prototype chain
  • Adding to a prototype anywhere in the prototype chain will add property all the way up the chain.
  • hasOwnProperty() will tell you if the method is defined on the current object or inherited.
  • Inheritance ONLY inherits prototypes not constructor methods.
  • Example: forEach example to support legacy browsers
if (!Array.prototype.forEach) {
 Array.prototype.forEach = function(callback, context) {
     for (var i = 0; i < this.length; i++) {
         callback.call(context || null, this[i], i, this);
     }
 };
}

Prototypal Inheritance

Prototypes have a "chain" all the way up to Object

When a function is called on an instance of an object:

  1. Check for method on instance
  2. Check for method on current instance prototype
  3. Check for method on prototype of object inherited from.
  4. Continue up the chain until there is no further prototype

This is why ALL functions have toString(), call(), apply(), bind() methods.


//Constructor logic to set object properties, NOT prototype properties
function Song(){
    this.play = function() {
        throw new Error('Not implemented. Need to inherit into genre.');
    }
} 

Song.prototype.play = function() {
    return 'playing';
}

function MetalSong(){
    this.volume = 11;
}

//prototype is an object
MetalSong.prototype = Object.create(Song.prototype, {
  constructor : MetalSong
});

function DeathMetalSong(){
    this.volume = Infinity.max;
}

DeathMetalSong.prototype = Object.create(MetalSong.prototype, {
  constructor : DeathMetalSong
});

var burningRottenLambs = new DeathMetalSong();

burningRottenLambs.play(); // 'playing'



Prototypal Inheritance

Prototypes have a "chain" all the way up to Object

//Constructor logic to set object properties, NOT prototype properties
function Song(){
    this.play = function() {
        throw new Error('Not implemented. Need to inherit into genre.');
    }
} 

Song.prototype.play = function() {
    return 'playing';
}

function MetalSong(){}

//prototype is an object
MetalSong.prototype = Object.create(Song.prototype, {
  constructor : MetalSong
});

function DeathMetalSong(){}

DeathMetalSong.prototype = Object.create(MetalSong.prototype);

function climbTheRopes(obj){
  console.log('play() is mine: ' + obj.hasOwnProperty('play'));
  console.log('Prototype is: ' + Object.getPrototypeOf(obj));
//   console.log(obj.__proto__); //this is the only time you would EVER need/use proto
  
  if(!Object.getPrototypeOf(obj)){
    return;
  }
              
  climbTheRopes(Object.getPrototypeOf(obj));
}

var deadSilence = new DeathMetalSong();
climbTheRopes(deadSilence);

passing args up the chain

function Song(){
  this.play = function() {
    return 'Hi-jacked';
  }
}

Song.prototype.play = function() {
  return 'playing';
}

function MetalSong(title){
  this.title = title
  this.volume = 11;
}

MetalSong.prototype = Object.create(Song.prototype, {
  constructor : MetalSong
});

function DeathMetalSong(title) {
  // To pass args up to the prototype
  // invoke call and pass in the args
  MetalSong.call(this, title)
  this.volume = Infinity;
}

DeathMetalSong.prototype = Object.create(MetalSong.prototype, {
  constructor : DeathMetalSong
});

let rainInBlood = new DeathMetalSong('rain In Blood');
console.log('rainInBlood.title: ', rainInBlood.title); //rain in blood

Things to know

  • NEVER extend the Object prototype.
  • Prototype definitions are not hoisted because of assignment
  • __proto__ : now forget about it.
  • Since a constructor is just a function you can call it like a method without new.
    • Name your classes upper case as a visual signal.
    • This will make the context of the object the global scope.
    • Example of human proofing:
function User(first, last) {
 if (!(this instanceof arguments.callee)) {
     return new User(first,last);
 }
 this.name = first + " " + last;
}

Additional Resources

Prototypal Inheritance and The Chain Gang

By Joe Karlsson

Prototypal Inheritance and The Chain Gang

  • 793
Loading comments...

More from Joe Karlsson