Using Prototype Property in JavaScript

Tags:

I have been asked to review the book “Object-Oriented JavaScript” by Stoyan Stefanov, and published by Packt Publishing. The review is not quite ready yet, but in the meantime, here’s some exclusive content from the book for you to have a look at.

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”


Object-Oriented JavaScript

Object-Oriented JavaScript Create scalable, reusable high-quality JavaScript applications and libraries

  • Learn to think in JavaScript, the language of the web browser
  • Object-oriented programming made accessible and understandable to web developers
  • Do it yourself: experiment with examples that can be used in your own scripts
  • Write better, more maintainable JavaScript code

For more information, please visit:
http://www.PacktPub.com/object-oriented-javascript-applications-libraries/book


Enumerating Properties

If you want to list all properties of an object, you can use a for-in loop. In
Chapter 2, you saw how you could loop through all the elements of an array:

var a = [1, 2, 3];
for (var i in a) {
  console.log(a[i]);
}

Arrays are objects, so you can expect that the for-in loop works for objects
too:

var o = {p1: 1, p2: 2};
for (var i in o) { 
  console.log(i + '=' + o[i]);
}

This produces:

p1=1

p2=2

There are some details to be aware of:

  • Not all properties show up in a for-in loop. For example, the length (for
    arrays) and constructor properties will not show up. The properties that do show up are
    called enumerable. You can check which ones are enumerable with the help of the
    propertyIsEnumerable() method that everyobject provides.
  • Prototypes that come through the prototype chain will also show up, provided they are
    enumerable. You can check if a property is an own property versus prototype’s using the
    hasOwnProperty() method.
  • propertyIsEnumerable() will return false for all of the prototype’s
    properties, even those that are enumerable and will show up in thefor-in
    loop.

Let’s see these methods in action. Take this simplified version of
Gadget():

function Gadget(name, color) {
  this.name = name;
  this.color = color;
  this.someMethod = function(){return 1;}
}
Gadget.prototype.price = 100;
Gadget.prototype.rating = 3;

Creating a new object:

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

Now if you loop using a for-in, you see of the object’s all properties,
including those that come from the prototype:

for (var prop in newtoy) { 
  console.log(prop + ' = ' + newtoy[prop]); 
}

The result also contains the object’s methods (as methods are just properties that
happen to be functions):

name = webcam

color = black

someMethod = function () { return 1; }

price = 100

rating = 3

If you want to distinguish between the object’s own properties versus the prototype’s
properties, use hasOwnProperty(). Try first:

>>> newtoy.hasOwnProperty('name')

true

>>> newtoy.hasOwnProperty('price')

false

Let’s loop again, but showing only own properties:

for (var prop in newtoy) { 
  if (newtoy.hasOwnProperty(prop)) { 
   console.log(prop + '=' + newtoy[prop]); 
  }
}

The result:

name=webcam

color=black

someMethod=function () { return 1; }

Now let’s try propertyIsEnumerable(). This method returns true for own
properties that are not built-in:

>>> newtoy.propertyIsEnumerable('name')

true

Most built-in properties and methods are not enumerable:

>>> newtoy.propertyIsEnumerable('constructor')

false

Any properties coming down the prototype chain are not enumerable:

>>> newtoy.propertyIsEnumerable('price')

false

Note, however, that such properties are enumerable if you reach the object contained
in the prototype and invoke its propertyIsEnumerable().

>>> newtoy.constructor.prototype.propertyIsEnumerable('price')

true

isPrototypeOf()

Every object also gets the isPrototypeOf() method. This method tells you
whether that specific object is used as a prototype of another object.

Let’s take a simple object monkey.

var monkey = {   
  hair: true,   
  feeds: 'bananas',   
  breathes: 'air' 
};

Now let’s create a Human() constructor function and set its prototype property
to point to monkey.

function Human(name) {   
  this.name = name;
}
Human.prototype = monkey;

Now if you create a new Human object called george and ask: “Is
monkey george’s prototype?”, you’ll get true.

>>> var george = new Human('George'); 
>>> monkey.isPrototypeOf(george)

true

The Secret __proto__ Link

As you know already, the prototype property will be consulted when you try to access a
property that does not exist in the current object.

Let’s again have an object called monkey and use it as a prototype when
creating objects with the Human() constructor.

var monkey = {  
  feeds: 'bananas',  
  breathes: 'air'
};
function Human() {} 
Human.prototype = monkey;

Now let’s create a developer object and give it some properties:

var developer = new Human();
developer.feeds = 'pizza';
developer.hacks = 'JavaScript';

Now let’s consult some of the properties. hacks is a property of thedeveloper
object.

>>> developer.hacks

“JavaScript”

feeds could also be found in the object.

>>> developer.feeds

“pizza”

breathes doesn’t exist as a property of the developer object, so the
prototype is looked up, as if there is a secret link pointing to the prototype
object.

>>> developer.breathes

“air”

Can you get from the developer object to the prototype object? Well, you could, using
constructor as the middleman, so having something like
developer.constructor.prototype should point to monkey. The problem is that
this is not very reliable, because constructor is more for informational purposes and can
easily be overwritten at any time. You can overwrite it with something that’s not even an
object and this will not affect the normal functioning of the prototype chain.

Let’s set the constructor property to some string:

>>> developer.constructor = 'junk'

“junk”

It seems like the prototype is now all messed up:

>>> typeof developer.constructor.prototype

“undefined”

…but it isn’t, because the developer still breathes “air”:

>>> developer.breathes

“air”

This shows that the secret link to the prototype still exists. The secret link is
exposed in Firefox as the __proto__ property (the word “proto” with two
underscores before and two after).

>>> developer.__proto__

Object feeds=bananas breathes=air

You can use this secret property for learning purposes but it’s not a good idea to use
it in your real scripts, because it does not exist in Internet Explorer, so your scripts
won’t be portable. For example, let’s say you have created a number of objects with
monkey as a prototype and now you want to change something in all objects. You can
change monkey and all instances will inherit the change:

>>> monkey.test = 1

1

>>> developer.test

1

__proto__ is not the same as prototype. __proto__ is a property of the
instances, whereas prototype is a property of the constructor functions.

>>> typeof developer.__proto__

“object”

>>> typeof developer.prototype

“undefined”

Once again, you should use __proto__ only for learning or debugging
purposes.


Object-Oriented JavaScript

Object-Oriented JavaScript Create scalable, reusable high-quality JavaScript applications and libraries

  • Learn to think in JavaScript, the language of the web browser
  • Object-oriented programming made accessible and understandable to web developers
  • Do it yourself: experiment with examples that can be used in your own scripts
  • Write better, more maintainable JavaScript code

For more information, please visit:
http://www.PacktPub.com/object-oriented-javascript-applications-libraries/book