Inheritance and Prototype in JS

Objectives

  • Describe the concept of inheritance as it relates to computer programming.
    • Give real world examples of inheritance
  • Describe the role that the prototype object plays in JavaScript's inheritance pattern.
  • Implement inheritance using JavaScript
    • use an object literal
    • use Object.create
    • use new and a constructor function
  • Describe some benefits and pitfalls of using the prototype object

Inheritance / "is a" Pattern

  • Inheritance is a programmatic way of saying this object "is a" blank
    • A Golden Retriever is a dog
    • A square is a 2 dimensional shape
    • A senator is a politician
    • More?
       
  • This is a extremely important pattern in programming.
     
  • Examples for our examples:
    • all dogs can bark, therefore a Golden Retriever can bark.
    • all 2D shapes have an area, therefore a square has an area.
    • all politicians can lie, therefore a senator can lie.
    • More?

Inheritance is Architecture

var obj = {};

// obj inherits from something called "Object"
// as a result, it has some special functions!
console.log(obj.hasOwnProperty); // [Function: hasOwnProperty]
console.log(obj.toString());     // [Function: toString]

JavaScript uses inheritance for internal representations. Every object inherits from a base object.

 

For example, where did these two properties come from? We didn't define them ...

Inheritance is Architecture

console.log(Object); // [Function: Object]

// ALL objects inherit functionality from Object
// We can see that Object.hasOwnProperty and obj.hasOwnProperty
// point to the same location in memory.
var obj = {};
console.log(Object.hasOwnProperty === obj.hasOwnProperty);

There is a very special object in JavaScript named Object.

 

The capital O is important.

So why Inherit?

  • Inheritance lets us share common data and functionality.
     
  • Inheritance gives us a familiar mental model.
     
  • Inheritance in JS is often more efficient because it shares data across multiple objects, rather than duplicating it.

After the last example, I hope it's clear that you have no choice but to use inheritance.

But there are other good reasons as well:

Alright, Lets Build Some

minionOne = {
    sayBanana: function() {
      return "banana";
    }
};

minionTwo = {
    sayBanana: function() {
      return "banana";
    }
};

// "banana"
minionOne.sayBanana();

// "banana"
minionTwo.sayBanana();

Here, we had to explicitly give each minion a function to "sayBanana".

 

Whats bad about this?

An Example, Extreme Inheritance

Object.prototype.sayBanana = function() {
  return "banana";
};


{}.sayBanana();

var gollum = {};

gollum.sayBanana();

// Arrays inherit from Object
[].sayBanana();

Here, we give EVERY object the function sayBanana.

 

Array's inherit from Object, so because we extended Object, we extended Array as well.

 

Typically, we want something between these two extremes.

An Example, Inheritance but less Extreme

var prototypeObject = {
  sayBanana: function() {
    return "banana";
  }
};

var minionOne = Object.create(prototypeObject);
var minionTwo = Object.create(prototypeObject);

console.log(minionTwo.sayBanana());
console.log(minionOne.sayBanana());

[].sayBanana() // throws an error now

This example is a better mix. This time, we give 'minions' a prototype explicitly.

 

This way we don't have to modify the global Object prototype.

 

So, what does this tell us about Object.create?

Object.create

var prototypeObject = {
  sayBanana: function() {
    return "banana";
  }
};

var minionOne = Object.create(prototypeObject);
var minionTwo = Object.create(prototypeObject);

console.log(minionTwo.sayBanana());
console.log(minionOne.sayBanana());

[].sayBanana() // throws an error now

Object.create is a function which returns an object, and takes an object as a parameter.

 

The object it returns will be an empty object with a prototype of whatever object was passed to Object.create.

 

 

Inheritance but less Extreme using a Constructor

function Minion() {
}

Minion.prototype.sayBanana = function() {
  return "banana";
};

var minionOne = new Minion();
var minionTwo = new Minion();

// "banana"
minionOne.sayBanana();

// "banana"
minionTwo.sayBanana();

We also can use a constructor function and alter the prototype of that function.

 

Notice the new keyword, this creates an object and the objects prototype will be the constructor function's prototype.

 

By convention, if a function is a constructor, it's name is capitalized

Constructor Functions

function Minion() {
    this.dataPoint = "I am a Minion";
}

Minion.prototype.sayBanana = function() {
  return "banana";
};

var minionOne = new Minion();
var minionTwo = new Minion();

minionOne.sayBanana();
minionTwo.sayBanana();

minionOne.dataPoint = "I have revolted";
console.log(minionOne.dataPoint);
console.log(minionTwo.dataPoint);

Constructor functions return 'this' as a copy. The copy can be changed.

Constructor Functions

function Minion() {
    this.dataPoint = "I am a Minion";
}

Minion.prototype.sayBanana = function() {
  return "banana";
};

var minionOne = new Minion();
var minionTwo = new Minion();

minionOne.sayBanana = function() {
    return "HAHAHAHAHAHAHAHA - ANARCHY";
}

console.log(minionOne.sayBanana());
console.log(minionOne.__proto__.sayBanana())
console.log(minionTwo.sayBanana());

Copies can overwrite their prototype. You can access the prototype directly with __proto__

Questions?

Inheritance in JS

By Tyler Bettilyon

Inheritance in JS

  • 1,297