Skip to content

Instantly share code, notes, and snippets.

@mariapacana
Last active December 20, 2015 04:49
Show Gist options
  • Save mariapacana/6074180 to your computer and use it in GitHub Desktop.
Save mariapacana/6074180 to your computer and use it in GitHub Desktop.
Javascript and Inheritance

Objects

  • Objects in Javascript are essentially hashes.
  • There are special types of objects such as strings, integers, and functions, but a 'plain Javascript object' is just a hash consisting of key-value pairs.
  • Keys must be strings. Values can be any object, including functions.

Prototypes

  • Every object has a 'prototype' that it inherits from.
  • Prototypes are simply objects containing various properties.
  • When you call a property of an object, Javascript first looks inside the object to retrieve that property. If it doesn't find it, it looks inside the object's prototype.
  • You can only set an object's prototype once; it can never be overwritten.

Useful Tools

  1. dir: Shows all of the properties attached to an object.
object = {'a':true}
dir(object)
\\ Should output the below
Object
a: true
__proto__: Object
  1. hasOwnProperty: Shows whether a property belongs to an object or to its prototype.
object = {'a':true}
proto = {'b':true}
\\ Setting the prototype of object to proto
object.__proto__ = proto
object.hasOwnProperty('a')
\\ Should output 'true'
object.hasOwnProperty('b')
\\ Should output 'false'

Ways to Set an Object's Prototype

###1. Directly set the object's prototype using __proto__ (only in v8). The easiest way to establish inheritance, but only works in the v8 implementation of Javascript!

animal = {
  isAlive: true
}

mammal = {
  hasBlood: true
}

mammal.__proto__ = animal;

Note that .__proto__ is the only way you can directly see what properties are in an object's prototype. In implementations without .__proto__, you won't be able to do this.

mammal.__proto__;
\\ Should give you something like
Object {isAlive: true}

Check the object's properties using dir to see what it contains.

animal.hasBlood
\\ Should return true

mammal.isAlive
\\ Should return true

dir(mammal)

\\Should return something like the following
Object <- Below are the properties of the object.
hasBlood: true
__proto__: Object <- Below are the properties of the object's prototype.
isAlive: true
__proto__: Object

Note that .prototype doesn't actually point to an object's prototype. (There is a 'prototype' property for constructor functions, which we will cover in #3).

foo.prototype = bar
\\ This just creates a property 'prototype' with a value 'bar', without establishing inheritance!

###2. With Object.create This method works reliably regardless of the Javascript implementation.

animal = {
  isAlive: true
}

mammal = Object.create(animal);
mammal.hasBlood = true;

dir(mammal)
\\Should return something like the below.
Object
hasBlood: true
__proto__: Object
isAlive: true
__proto__: Object

Note that the two statements below are equivalent.

mammal = Object.create(animal);
mammal.__proto__ = animal;

###3. With a constructor function

This works reliably regardless of implementation. Another bonus is being able to use instanceof to see whether an object inherits from a constructor function's prototype.

Example 1: Animals!

animal = {
  isAlive: true
}

\\ All functions in Javascript have a 'prototype' property, which can be set to any object.
\\ In this case, the prototype property is being set to animal.
var Animal = function(){};
Animal.prototype = animal; 

mammal = new Animal; \\ Now mammal.__proto__ === Animal.prototype
mammal.hasBlood = true;

The two ways of initializing an object below are equivalent.

1. With 'new'
mammal = new Animal;

2. With Object.create and .call
mammal = Object.create(Animal.prototype);
Animal.apply(mammal); \\Scroll down to the last section for an explanation of 'apply'

Example 2: Trucks!

Setting up a constructor function for a truck.

truck = {
  drive:function(){
    console.log(this.color+' car go!');
  }
}

function Truck(color){
  this.color = color;
}

Truck.prototype = truck;

Below are two equivalent ways of creating trucks.

1. With 'new'
my_truck = new Truck('purple');

2. With Object.create and .call
your_truck = Object.create(Truck.prototype);
Truck.call(your_truck, 'green');

\\ Because 'this' is your_truck, and the argument passed in is 'green', 
\\ .call basically sets your_truck.color = 'green'.

Use instanceof to check whether an object inherits from the prototype of a constructor function.

my_truck instanceof Truck
\\ Should return true

####instanceof Can only be used with constructor functions.

my_truck instanceof truck
\\ Should result in an error!

Looks all the way up the prototype chain, meaning it compares the given constructor class to an object's prototype, but also to its prototype's prototype, and its prototype's prototype's prototype, etc.

vehicle = { hasWheels: true }
function Vehicle(){}
Vehicle.prototype = vehicle;
truck.__proto__ = vehicle;
my_truck instanceof Vehicle
\\ Should return true

####.constructor Every prototype of a constructor function has a 'constructor' property that takes you back to its constructor function.

var object = function(){this.A = true;}
object.prototype.constructor
\\ Should return object => function (){this.A = true;}
object.prototype.constructor === object;
\\ Should return true

Using Prototypes

  1. Don't do something like the below, because it overwrites the whole prototype each time.
Animal.prototype = { 
  drive: function() {};
};

Do this instead, setting the prototype's properties one at a time:

Animal.prototype.drive = function() {};

##Other Useful Things to Know

.apply, call, bind

All ways of setting 'this' for a function and passing in the provided arguments.

\\ .apply takes in an array of arguments, while .call and .bind don't use an array
.apply(object, [arg1, arg2, arg3])

.call(object, arg1, arg2, arg3)

\\ .bind sets 'this' permanently; every time the function is called, 'this' will always be the same.
.bind(object, arg1, arg2, arg3)

A useful scenario for .call (aside from the Truck example).

\\ Imagine that you have an object 'frog' with a method 'speak'.
\\ You want your object 'cat' to speak as well.
frog.speak.apply(cat)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment