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:
- 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). - 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:
- All of the properties that you do not want to be shared go inside of the constructor function
- 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