{ Prototypes. }

Objectives

By the end of this chapter, you should be able to:

  • Define what an object's prototype is
  • Explain what effect using the new keyword has on an object's prototype
  • Explain why functions should be added to the prototype instead of the constructor function
  • Create a "class" in javascript that inherits from a parent "class"

Prototype Intro

Every single function that is created in JavaScript has a prototype property. Moreover, each object that is created can access its constructor's prototype property via the object's own __proto__ property.

Let's start by looking at Object.prototype. In the Chrome console, try typing Object.prototype then expand the object you get back. You can see that Object already has many properties on its prototype.

When you create a constructor function, that function will have it's own prototype. Let's try that out by creating a Person constructor function:

function Person(name) {
   this.name = name;
}

var tim = new Person("Tim");

Person.prototype; // Object {}

So far, our Person constructor function has a prototype and the only two properties available on the prototype should be constructor and __proto__. Let's try adding a function to the Person prototype:

Person.prototype.sayHello = function() {
    return "Hello, " + this.name;
};

Now that we have added sayHello to the prototype of Person, any person object that will be create or that was created in the past has access to the function:

var moxie = new Person("Moxie");
moxie.sayHello();  // returns "Hello, Moxie"

// Notice that sayHello still works for tim even though tim was created
// before the sayHello function was added to the prototype.
tim.sayHello();    // returns "Hello, Tim"

So the main things to know so far about an Object's prototype are the following:

  1. Any function or property added to the prototype is shared among all instances linked to that prototype (For example, sayHello is shared among all Person instances).
  2. Each constructor function has its own prototype.

This video discusses prototypes in a bit more detail:

Shared Prototype Example

Let's look at another example of adding properties to a prototype.

function Person(name){
    this.name = name;
}

Person.prototype.siblings = ["Haim", "David"];

var elie = new Person("Elie");

The above example creates a instance of a Person and sets a siblings array on the prototype. The intention is for elie to have an array of siblings. However, since the prototype is shared among all instances of a Person, any other instance will also have access to the same siblings array:

elie.siblings.push("Tamar"); // returns the new length of the array => 3
// The siblings array will now be ["Haim", "David", "Tamar"]

var anotherPerson = new Person("Mary");

anotherPerson.siblings.push("Leslie");
elie.siblings; // ["Haim", "David", "Tamar", "Leslie"]

We can see again from this example, that anything put on the prototype is shared among all instances of that object.

This video provides more detail on how objects can share properties and methods via a common prototype:

Constructor Function Best Practices

The best practices for creating constructor functions in JavaScript are:

  1. All of the properties that you do not want to be shared go inside of the constructor function
  2. All properties that you want to be shared go on the prototype. Almost all of the time, you will want to put functions on the prototype. We will explain why soon!

Using our person example, if we want to add a siblings array to the Person class, we would add it in the constructor:

function Person(name) {
    this.name = name;
    this.siblings = [];
}

var janey = new Person("Janey");
janey.silbings.push("Annie");

Now each time the new keyword is used on the Person constructor, a new object is created that has its own name and siblings property. Now if we create another person it will have its own name and siblings array as well:

var tim = new Person("Tim");
tim.siblings.push("Nicole");
tim.siblings.push("Jeff");
tim.siblings.push("Greg");
tim.siblings; // Returns ["Nicole", "Jeff", "Greg"];

We said earlier that when it comes to functions, you typically want to add them to the prototype. Why is this? After all, your code will function correctly if you create your function definitions in the constructor like this:

// NOT A GOOD PRACTICE
function Person(name) {
  this.name = name;
  this.sayHi = function() {
    return "Hello, " + this.name;
  }
}

The problem is that every time you use the new keyword to create a Person, a new object gets created in memory that allocates space for the person's name and also for the sayHi function. So if we have 10 Person objects that we create, there will be 10 copies of the same sayHi function. Since the function does not need to be unique per Person instance, it is better to add the function to the prototype, like this:

// BEST PRACTICE!!
function Person(name) {
   this.name = name;
}

Person.prototype.sayHi = function() {
    return "Hello, " + this.name;
}

Unless you have a good reason not to, always put function definitions on the constructor function's prototype.

JavaScript Property Lookup

When you attempt to access a property on an object in JavaScript, there is a lookup process that goes on in order to find your property. To find the value for a property, first the properties on the object are checked. If the property is not found, then the properties on the prototype of the constructor function are checked. Let's look at an example:

function Automobile(make, model, year) {
    this.make = make;
    this.model = model;
    if (year !== undefined) {
        this.year = year;
    }
}

Automobile.prototype.year = 2016;

Notice that year is set on the prototype to 2016. Also, if no year is passed into the constructor, an assignment to year will not be made.

var newCar = new Automobile("Ferrari", "488 Spider");

// In this case, we did not pass in a year,
// so it was never set in the constructor function
newCar.hasOwnProperty("year"); // Returns false

newCar.year; // returns 2016

Now, if we create a car with a year, the property on the car object will be seen first in the property lookup:

var probe = new Automobile("Ford", "Probe", 1993);

probe.hasOwnProperty("year"); // returns true

probe.year; // returns 1993

These videos offer some exercies (and solutions) on constructor functions and prototypes:

Exercise

Complete the Prototypes Exercise

When you're ready, move on to Inheritance

Continue