7 min read

In this article by Stoyan Stefanov, you’ll learn about the prototype property of the function objects. Understanding how the prototype works is an important part of learning the JavaScript language. After all, JavaScript is classified as having a prototype-based object model. There’s nothing particularly difficult about the prototype, but it is a new concept and as such may sometimes take some time to sink in. It’s one of these things in JavaScript (closures are another) which, once you “get” them, they seem so obvious and make perfect sense. As with the rest of the article, you’re strongly encouraged to type in and play around with the examples; this makes it much easier to learn and remember the concepts.

The following topics are discussed in this article:

  • Every function has a prototype property and it contains an object
  • Adding properties to the prototype object
  • Using the properties added to the prototype
  • The difference between own properties and properties of the prototype
  • __proto__, the secret link every object keeps to its prototype
  • Methods such as isPrototypeOf(), hasOwnProperty(), and propertyIsEnumerable()

The prototype Property

The functions in JavaScript are objects and they contain methods and properties. Some of the common methods are apply() and call() and some of the common properties are length and constructor. Another property of the function objects is prototype.

If you define a simple function foo() you can access its properties as you would do with any other object:

>>>function foo(a, b){return a * b;}
>>>foo.length

2

>>>foo.constructor

Function()

prototype is a property that gets created as soon as you define the function. Its initial value is an empty object.

>>>typeof foo.prototype

“object”

It’s as if you added this property yourself like this:

>>>foo.prototype = {}

You can augment this empty object with properties and methods. They won’t have any effect of the foo() function itself; they’ll only be used when you use foo()as a constructor.

Adding Methods and Properties Using the Prototype

Constructor functions can be used to create (construct) new objects. The main idea is that inside a function invoked with new you have access to the value this, which contains the object to be returned by the constructor. Augmenting (adding methods and properties to) this object is the way to add functionality to the object being created.

Let’s take a look at the constructor function Gadget() which uses this to add two properties and one method to the objects it creates.

function Gadget(name, color) { 
  this.name = name;
  this.color = color;
  this.whatAreYou = function(){
   return 'I am a ' + this.color + ' ' + this.name;
  }
}

Adding methods and properties to the prototype property of the constructor function is another way to add functionality to the objects this constructor produces. Let’s add two more properties, price and rating, and a getInfo() method. Since prototype contains an object, you can just keep adding to it like this:

Gadget.prototype.price = 100;
Gadget.prototype.rating = 3;
Gadget.prototype.getInfo = function() {
  return 'Rating: ' + this.rating + ', price: ' + this.price;
};

Instead of adding to the prototype object, another way to achieve the above result is to overwrite the prototype completely, replacing it with an object of your choice:

Gadget.prototype = { 
  price: 100,
  rating: 3,
  getInfo: function() {
   return 'Rating: ' + this.rating + ', price: ' + this.price;
  }
};

Using the Prototype’s Methods and Properties

All the methods and properties you have added to the prototype are directly available as soon as you create a new object using the constructor. If you create a newtoy object using the Gadget() constructor, you can access all the methods and properties already defined.

>>> var newtoy = new Gadget('webcam', 'black');
>>> newtoy.name;

“webcam”

>>> newtoy.color;

“black”

>>> newtoy.whatAreYou();

“I am a black webcam”

>>> newtoy.price;

100

>>> newtoy.rating;

3

>>> newtoy.getInfo();

“Rating: 3, price: 100”

It’s important to note that the prototype is “live”. Objects are passed by reference in JavaScript, and therefore the prototype is not copied with every new object instance. What does this mean in practice? It means that you can modify the prototype at any time and all objects (even those created before the modification) will inherit the changes.

Let’s continue the example, adding a new method to the prototype:

Gadget.prototype.get = function(what) { 
  return this[what];
};

Even though newtoy was created before the get() method was defined, newtoy will still have access to the new method:

>>> newtoy.get('price');

100

>>> newtoy.get('color');

“black”

Own Properties versus prototype Properties

In the example above getInfo() used this internally to address the object. It could’ve also used Gadget.prototype to achieve the same result:

Gadget.prototype.getInfo = function() { 
  return 'Rating: ' + Gadget.prototype.rating + ', price: ' + Gadget.prototype.price;
};

What’s is the difference? To answer this question, let’s examine how the prototype works in more detail.

Let’s again take our newtoy object:

>>> var newtoy = new Gadget('webcam', 'black');

When you try to access a property of newtoy, say newtoy.name the JavaScript engine will look through all of the properties of the object searching for one called name and, if it finds it, will return its value.

>>> newtoy.name

“webcam”

What if you try to access the rating property? The JavaScript engine will examine all of the properties of newtoy and will not find the one called rating. Then the script engine will identify the prototype of the constructor function used to create this object (same as if you do newtoy.constructor.prototype). If the property is found in the prototype, this property is used.

>>> newtoy.rating

3

This would be the same as if you accessed the prototype directly. Every object has a constructor property, which is a reference to the function that created the object, so in our case:

>>> newtoy.constructor

Gadget(name, color)

>>> newtoy.constructor.prototype.rating

3

Now let’s take this lookup one step further. Every object has a constructor. The prototype is an object, so it must have a constructor too. Which in turn has a prototype. In other words you can do:

>>> newtoy.constructor.prototype.constructor

Gadget(name, color)

>>> newtoy.constructor.prototype.constructor.prototype

Object price=100 rating=3

This might go on for a while, depending on how long the prototype chain is, but you eventually end up with the built-in Object() object, which is the highest-level parent. In practice, this means that if you try newtoy.toString() and newtoy doesn’t have an own toString() method and its prototype doesn’t either, in the end you’ll get the Object’s toString()

>>> newtoy.toString()

“[object Object]”

Overwriting Prototype’s Property withOwn Property

As the above discussion demonstrates, if one of your objects doesn’t have a certain property of its own, it can use one (if exists) somewhere up the prototype chain. What if the object does have its own property and the prototype also has one with the same name? The own property takes precedence over the prototype’s.

Let’s have a scenario where a property name exists both as an own property and as a property of the prototype object:

function Gadget(name) { 
  this.name = name;
}
Gadget.prototype.name = 'foo';

“foo”

Creating a new object and accessing its name property gives you the object’s ownname property.

>>> var toy = new Gadget('camera');
>>> toy.name;

“camera”

If you delete this property, the prototype’s property with the same name”shines through”:

>>> delete toy.name;

true

>>> toy.name;

“foo”

Of course, you can always re-create the object’s own property:

>>> toy.name = 'camera';
>>> toy.name;

“camera”

 


Subscribe to the weekly Packt Hub newsletter. We'll send you this year's Skill Up Developer Skills Report.

* indicates required

LEAVE A REPLY

Please enter your comment!
Please enter your name here