Watch our 3-minute video to find out how you can learn JavaScript with a live instructor.
Additional Resources

Object Oriented Aspects of JavaScript

In this lesson of the JavaScript tutorial, you will learn...
  1. That JavaScript is, in fact, object oriented.
  2. To create new objects in a variety of ways.
  3. To be careful with the this keyword.
  4. The difference between call() and apply().
  5. To extend existing objects.

Yes, JavaScript is Object Oriented

If you are a web developer, chances are you work with an object oriented language on the server side, be it Java, C#, Ruby, VB, etc. You may also already understand the advantages of using an OO programing language over a purely procedural one.

In this lesson and the next one we will provide quick recipes of how common OO techniques are implemented with JavaScript. We will also present some additional techniques that may not be available in other OO languages that you are familiar with.

In this lesson we will cover most of the OO concepts as implemented in JavaScript. The screaming exception is Inheritance, which we we will defer to the next lesson.

Why Create JavaScript Objects?

Let's get back to our server code once again. If you are writing an application that reports data from a database, you are probably dealing with objects such as database connections, recordsets, error loggers, etc. You may also have custom objects that represent entities from your problem domain, like Order, Employee, Company, PriceQuote, etc.

You need those objects because they encapsulate a lot of functionality that we can reuse over and over. It should not be surprising that soaring demand for Rich Internet Applications (RIA), the need for objects to assist with the UI logic also increases.

What we are suggesting here is that it is much more appropriate to produce reusable JavaScript objects than vast collections of JavaScript functions. The problem with the functions, as we know from procedural programming languages, is that they are either too complex to use (because they want to solve all the possible variants of the problem) or they need to be called in a specific sequence.

Imagine that you have the common scenario of cascading drop down lists, one with a list of countries, the other with states or provinces. When the country changes you want to update the states list accordingly.

If we were doing this in a procedural fashion, we would create a function to handle the change event of the countries list, in this function we would call another function, passing the country name to refresh the states list. This second function would receive the country name, retrieve the states list and update the states drop down.

Code Sample: ObjectOrientedJS/Demos/cascading-original.html

---- Code Omitted ----
function countryChanged() { var sel = document.getElementById("country"); var country = sel.options[sel.selectedIndex].value; updateStateList(country); } function updateStateList(country) { var states = STATES_PER_COUNTRY[ country ]; var list = document.getElementById("state"); list.options.length = 0; for (var i=0; i<states.length; i++) { list.options.add( new Option(states[i]) ); } } document.getElementById("country").onchange = countryChanged;
---- Code Omitted ----

A few days later we may need to implement a similar scheme, but this time with a Company/Employee pair of drop downs. Here we go writing all that code again.

If we were using an OO approach, we could create an object called CascadingListPair that knows how to capture the change events of the first drop down, call some code to retrieve the list for the second drop down, and update the second dropdown.

Code Sample: ObjectOrientedJS/Demos/cascading-objects.html

---- Code Omitted ----
var helper = new CascadingListPair( "country", "state", function (country) { return STATES_PER_COUNTRY[ country ]; });
---- Code Omitted ----

Don't worry about how complex the above code looks right now. We will discuss what is going on during this lesson. For now just appreciate how much shorter this is and how much logic you did not have to write.

Now if we were to create a cascading pair for Company/Employee, it would be so much simpler.

var helper2 = new CascadingListPair(
   "company", 
   "employee", 
   function (company) {
    return EMPLOYEES_PER_COMPANY[ company ];
   });

Creating Simple Objects

Objects are the fundamental unit of code encapsulation and reuse in any OO language. It is incredibly easy to create objects in JavaScript. There's even more than one way to do so.

1 - Building objects

The first approach is to create an empty object and progressively add its properties and methods.

Code Sample: ObjectOrientedJS/Demos/building-objects.html

---- Code Omitted ----
var GUITAR = { }; GUITAR.color = 'black'; GUITAR.strings = ['E', 'A', 'D', 'G', 'B', 'e']; GUITAR.tune = function (newStrings) { this.strings = newStrings; }; GUITAR.play = function (chord) { alert('Playing chord: ' + chord); }; GUITAR.print = function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); }; //using the object GUITAR.play('Dm7'); GUITAR.tune( ['D', 'A', 'D', 'G', 'B', 'e' ] ); debugWrite('this guitar is: ' + GUITAR.color); GUITAR.print(850, 'USD');
---- Code Omitted ----

The above methodology isn't too hard to understand but it is certainly more work than we are used to in more popular programming languages. What we did here is quite simple. We just created the object and appended each property and method as desired.

2 - Declaring objects

JavaScript also has a literal notation for objects. The previous example could have been rewritten in literal notation as follows.

Code Sample: ObjectOrientedJS/Demos/declaring-objects.html

---- Code Omitted ----
var GUITAR = { color: 'black', strings: ['E', 'A', 'D', 'G', 'B', 'e'], tune: function (newStrings) { this.strings = newStrings; }, play: function (chord) { alert('Playing chord: ' + chord); }, print: function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); } }; //using the object GUITAR.play('Dm7'); GUITAR.tune( ['D', 'A', 'D', 'G', 'B', 'e' ] ); debugWrite('this guitar is: ' + GUITAR.color); GUITAR.print(850, 'USD');
---- Code Omitted ----

The syntax is easy to understand. It is a comma-delimited list of name: value pairs. Note that the method declaration is easy to be confused with a regular function declaration. Just remember that a function can be used as a value and that's what is happening here. You can also think of the methods as properties that contain a function as their values, if that helps you understand the notation.

JSON

JavaScript Object Notation, or JSON, is a subset of the literal notation that we just saw. JSON was first proposed by Douglas Crockford as a neutral way to represent and transport data, usually replacing XML.

JSON, just like the literal notation, is also a list of name/value pairs. The main difference is that the values can only be a string, an Array, a Number, true, false, null, or another JSON object. The field names are also enclosed in double-quotes.

Here's the GUITAR object represented in JSON. Note that we cannot represent the methods because JSON doesn't accept them. It makes sense because JSON is meant only for data interchange, where behaviors are irrelevant.

var GUITAR = {
 "color":"black",
 "strings":['E', 'A', 'D', 'G', 'B', 'e']
};

3 - Using factory functions

One important thing to notice in the previous two approaches is that we did not need to create a formal class to serve as the template of the GUITAR object. If we needed a second guitar object we would need to create it the same way we did for the first one.

We could just encapsulate that logic in a function that can create a brand new guitar object on demand.

Code Sample: ObjectOrientedJS/Demos/factory-functions.html

---- Code Omitted ----
function createGuitar(color, strings) { var guitar = { }; guitar.color = color; guitar.strings = strings; guitar.tune = function (newStrings) { this.strings = newStrings; }; guitar.play = function (chord) { alert('Playing chord: ' + chord); }; guitar.print = function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); }; return guitar; } var GUITAR1 = createGuitar('black', ['E', 'A', 'D', 'G', 'B', 'e'] ); var GUITAR2 = createGuitar('maple', ['F', 'Bb', 'D#', 'G#', 'C', 'f'] );
---- Code Omitted ----

4 - Constructors

There's a variation of the factory function methodology that may feel more natural to you. In JavaScript, when function is called preceded by the new operator, the function receives an implicit this argument that is a brand new object, ready to be assembled with properties and methods. Also, if we do not return anything explicitly, the new operator automatically returns this.

Let's rework our last example into a constructor. A good convention is to start constructor functions with a capital letter, to differentiate from a regular function, signaling to the programmer that it needs to be called with the new operator.

Code Sample: ObjectOrientedJS/Demos/constructors.html

---- Code Omitted ----
function Guitar(color, strings) { this.color = color; this.strings = strings; this.tune = function (newStrings) { this.strings = newStrings; }; this.play = function (chord) { alert('Playing chord: ' + chord); }; this.print = function (price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); }; } var GUITAR = new Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e']); debugWrite('this guitar is: ' + GUITAR.color); GUITAR.play('Dm7'); GUITAR.tune( ['D', 'A', 'D', 'G', 'B', 'e' ] ); GUITAR.print(850, 'USD');
---- Code Omitted ----

The biggest differences here are two. First we no longer need to create the new object because the new operator has already taken care of that and passed the new object under the this identifier. The other difference is that we will use this wherever we were using guitar before. We could have returned this but that, as we explained above, is no longer necessary.

Exercise: Creating objects to encapsulate behaviors

Duration: 30 to 60 minutes.

In this exercise we will implement the ever popular Yellow Fade Technique, where we set the background color of an element to yellow and use timeouts to progressively fade the background back to white.

The exercise starts with working code that is not object oriented and has some problems because of that. We will convert the existing code into an object type that has the same functionality and fixes the original shortcomings.

  1. Open ObjectOrientedJS/Exercises/yellow-fade.html for editing.
  2. Change the JavaScript code to get rid of the functions highLightElement(), increaseYellow(), and setYellowLevel(), moving all that functionality into a constructor function called HighLightEffect().

Code Sample: ObjectOrientedJS/Exercises/yellow-fade.html

<html>
<head>
 <title>Yellow Fade</title>
 <style type="text/css">
  .wc_debug
  {
   background-color:#ffc;
  }
 </style>
 <script type="text/javascript" src="../../Libraries/DebugHelp.js" ></script>
</head>

<body>
 <h1>Yellow Fade</h1>
 <p>
  Add items by name to your shopping cart.
 </p>
 <h2>Shopping cart</h2>
 <table id="shoppingCart">
  <tr><th>Item</th><th>price</th><th></th></tr>
  <tr id="newItemRow" style="background-color:#ddd;">
   <td colspan="3"><b>New item:</b></td>
  </tr>
  <tr>
   <td><input type="text" id="itemName" /></td>
   <td><input type="text" id="itemPrice" size="5" /></td>
   <td><input type="button" id="addItem" value="Add" onclick="addingItem();" /></td>
  </tr>
 </table>
  
 <script type="text/javascript">
  function addingItem() {
   var name = document.getElementById('itemName').value;
   var price = document.getElementById('itemPrice').value;
   var newItemRow = document.getElementById('newItemRow');
   var table = document.getElementById('shoppingCart');
   var shoppingCart = table.getElementsByTagName("tbody")[0];
   var row = document.createElement('tr');

   addCellToRow(row, name);
   addCellToRow(row, price);
   var buttonCell = addCellToRow(row, '');
   var button = document.createElement('input');
   button.type = 'button';
   button.value = 'remove';
   button.onclick = removingItem;
   buttonCell.appendChild(button);
   
   shoppingCart.insertBefore(row, newItemRow);
   
   highLightElement(row);
   
  }
  
  function addCellToRow(row, cellText) {
   var cell = document.createElement('TD');
   
   cell.innerHTML = cellText;
   row.appendChild(cell);
   return cell;
  }
  
  function removingItem() {
   //'this'  it the remove button
   var row = this.parentNode.parentNode;
   row.parentNode.removeChild(row);
  }
  
  /*
   All the above code can stay. The only thing we are
   interested in this exercise is to replace the
   functions below this comment with an object that
   does the same thing.
   Instead of doing this in addingItem():
    highLightElement( row );
   we want to write:
    new HighLightEffect( row )
   challenge:
   -- Add extra properties and constructor parameters
     to the object to control the duration of the effect
   challenge 2:
   -- Add more properties to configure the start and end color of the effect
   
  */
  
  var LEVEL = 128;
  var ELEMENT = null;
  var INTERVAL = 100;
  
  function highLightElement(element) {
   ELEMENT = element;
   LEVEL = 128;
   setYellowLevel(element, LEVEL);
   setTimeout(increaseYellow, INTERVAL);
  }
  
  function increaseYellow() {
   LEVEL += 10;
   if (LEVEL > 255) {
    LEVEL = 255;
   }
   setYellowLevel(ELEMENT, LEVEL);
   if (LEVEL < 255) {
    setTimeout(increaseYellow, INTERVAL);
   }
  }
  
  function setYellowLevel(element, level) {
   var hex = level.toString(16);
   element.style.backgroundColor = '#ffff' + hex;
  }
  

 </script>
 
 <script type="text/javascript">
  insertDebugPanel();
 </script>
 
</body>
</html>
  1. Change the HighLightEffect() function to take an extra parameter to configure the length of the effect in seconds.
  2. Make the new parameter optional with a default value of 1 second.
  3. Change the example to pass in 2 seconds when creating the object.
  1. Change the HighLightEffect() function to take another two extra parameters to configure start and end colors of the effect.
  2. Create a method that splits the RGB color components in a 3-element array with integer values.
  3. In each timeout step, calculate the next value for each RGB component of the background color and set it.
  4. Make these two extra parameters optional and default them to "#ffff99" and "#ffffff" respectively.
  5. Change the background color of the shopping cart table to "#80ffff" and change the sample to use the start color of "#cc99cc" and end color the same as this new background.

Memory usage

One thing is common in all the presented ways to create an object may go unnoticed. The play() and tune() methods being added to each objects are always recreated from scratch for each object instance.

This will result in multiple copies of identical methods, which is clearly waste of memory. It may be a negligible waste for those tiny methods and in just a couple of object instances, but it's generally not a good practice.

We will present the most adequate solution in the next lesson, but there's a middle-of-the-road alternative that we can use to address the duplication issue in the meantime.

The idea is to create a single function for each of those methods and use them in the creation of the instance methods. The example below illustrates this.

Code Sample: ObjectOrientedJS/Demos/reusing-functions.html

---- Code Omitted ----
function tuneGuitar(newStrings) { this.strings = newStrings; } function playGuitar(chord) { alert('Playing chord: ' + chord); } function printInfo(price, currency) { alert('This guitar is ' + this.color + ', it has ' + this.strings.length + ' strings' + ' and it costs ' + price + currency); } function Guitar(color, strings) { this.color = color; this.strings = strings; this.tune = tuneGuitar; this.play = playGuitar; this.print = printInfo; } var GUITAR = new Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e'] );
---- Code Omitted ----

This reduces the number of identical functions created but it has a different cost. The code now looks more dispersed. The functions defined outside of the constructor method don't give any hint that they are used as methods of the Guitar objects.

Although the object instantiation via constructors may look as if we are defining classes, we will learn in the next lesson that this is not really true.

The perils of this

You may look at the previous example and wonder: What if someone calls tuneGuitar() directly, instead of through a Guitar object? Or what happens if the Guitar constructor is called without the new operator? Well, that is trouble indeed.

I JavaScript, most of the times, the this contains the object that owns the current method. One big exception are the constructor functions as we just saw. When we call a function directly like tuneGuitar( someArray ); there's no owning object for the stand alone tuneGuitar() function so this will be uninitialized. Or will it?

As it turns out, there is a default value for this, which is a global object. That object holds all our global variables as its properties and global functions as its methods. When we are running JavaScript in a browser this global object is the window object.

Back to our problem, if we call tuneGuitar() directly, we will end up adding a strings property to the window object, which is the same as creating a global variable called strings.

Even worse is when we call a constructor without the new operator. In this case we will create a series of global variables and functions. Another problem is that the variable that we are trying to assign the new object to will remain undefined:

var GUITAR1 = Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e']);
   debugWrite(GUITAR1);
   // => undefined

The bottom line here is that we have to make sure that when we a function has a this identifier then that function is always called as an object's method or that we are providing the value for this explicitly, as we will see now.

Calling or Applying functions

In JavaScript functions are 1st class objects of type Function. As such they have properties and methods of their own. Two of the methods are apply() and call().

We can use apply() and call() to invoke the function as if they were methods of whatever object we want. This is done by effectively setting the desired value of this.

Back once again to our Guitar objects and this time the printInfo() function.

function printInfo(price, currency) {
    alert('This guitar is ' +
     this.color +
     ', it has ' + this.strings.length + ' strings' +
     ' and it costs ' + price + currency);
   }

We already know that if we call this function directly, it will cause problems because this will be the window object. Since window doesn't have a strings property, the expression this.strings.length will fail.

Here's how we can overcome this problem.

var GUITAR1 = new Guitar('black', ['E', 'A', 'D', 'G', 'B', 'e']);
   printInfo.call( GUITAR1, '349.99', 'USD');
   // OR
   printInfo.apply( GUITAR1, ['349.99', 'USD']);

Both apply() and call() will produce the same result. The only difference is how the parameters are passed to the invoked function. apply() just expects all the parameters listed right after the target object. call() expects that the second parameter will be an array of the parameters to be passed to the function.

This difference can become useful when dealing with an unknown (or flexible) number of arguments.

Code Sample: ObjectOrientedJS/Demos/call-apply.html

---- Code Omitted ----
var UTILS = { getMaxLength: function () { var max = 0; for (var i=0; i<arguments.length; i++) { if (arguments[i].length > max) { max = arguments[i].length; } } return max; }, pad: function (text, maxLength) { var result = text; for (var i=text.length; i<maxLength; i++) { result = ' ' + result; } return result; }, alignRight: function () { var maxLength = this.getMaxLength.apply(this, arguments); var result = []; for (var i=0; i<arguments.length; i++) { result.push( this.pad(arguments[i], maxLength) ); } return result; } }; var ALIGNED = UTILS.alignRight('Homer', 'Leonard', 'Montgomery', 'Abe');
---- Code Omitted ----

Extending Existing Objects

When we showed how to create objects by building them, one thing that we did not mention but may go unnoticed is that we can add properties and methods to any object. It does not matter if the object was created by us or not.

To illustrate this, let's find a DOM element in a page, which is clearly an object not created by us, and let's extend it by adding new members.

Code Sample: ObjectOrientedJS/Demos/extending-objects.html

---- Code Omitted ----
<div> <div id='userName'>Joe Doe</div> <input type="button" value="Prepare" onclick="extend('userName', '#ffff99');" /> <input type="button" value="Highlight" onclick="show('userName');" /> </div> <script type="text/javascript"> function extend(elementName, color) { var el = document.getElementById(elementName); el.highlightColor = color; el.highlight = function () { this.style.backgroundColor = this.highlightColor; }; } function show(elementName) { var el = document.getElementById(elementName); el.highlight(); } </script>
---- Code Omitted ----

In this example the first button calls extend() to extend the userName div element. To extend the element, it tries to find the element in the document, if the element is found then a new property called highlightColor is added with the chosen color value. A new method is then added with the name highlight, which will simply set the element's background color style to the value contained in highlightColor.

The second button calls show(), which will once again find the element and call the brand new method highlight(). This causes the userName element to display a yellow background.

This type of object modification is an important characteristic of Dynamically Typed programming languages like JavaScript.

Merging Objects

The technique of adding new members to an existing object is so common in JavaScript libraries that some of them formalize and encapsulate this operation.

The code below is from the Prototype.js library and show the Object object itself being extended with a function that helps extending any object.

Object.extend = function(destination, source) {
 for (var property in source)
  destination[property] = source[property];
 return destination;
};

With this new method we can make the process of augmenting an existing object look more like we are merging it with a second one. Prototype.js uses this method many times in its own source code as shown below.

Object.extend(String.prototype, {
 escapeHTML: function() {
  return this.replace(/&/g,'&amp;').
     replace(/</g,'&lt;').
     replace(/>/g,'&gt;');
 },
 unescapeHTML: function() {
  return this.replace(/&amp;/g,'&').
     replace(/&lt;/g,'<').
     replace(/&gt;/g,'>');
 }
});

Dynamic Languages

A dynamic language is one where the type of the objects is only loosely bound to the way it was created. Objects may be created through a constructor method and inherit from a base class or a prototype but that doesn't mean that the object is locked for alterations.

Code can, at any time, add, remove or modify existing properties or methods of the objects. For that reason the base type of an object is less important in a dynamic language than it is in a statically typed language like Java or C#.

Duck Typing

A famous sentence reflects very well the importance given to base types in dynamic languages: "If it walks like a duck and quacks like a duck, I would call it a duck."

The gist of this statement is that in order for your code to work with a given object, you don't need the object to derive from a particular base type or prototype. You only need it to honor the expected public interface. As an example, as long as the given object has a method called sort(), your code will be able to call that method to sort the contents of the object. It doesn't matter if the object is an array or a DOM tree or a custom hash-like object.

That sounds Dangerous

You may think that dynamic typing is a recipe for disaster but in practice that's not the case. There are two important factors that make dynamic languages very appealing.

Productivity

How many times when programming in statically typed languages we were in the situation where we had this class that was almost perfect but it lacked one important property or method? The usual route is to inherit a new class from that one and add the missing functionality.

That works well in the beginning but it quickly leads to class explosion in the application. Before you notice you'll have dozens of classes that are minor improvements over other existing classes.

Dynamic languages offer the capability of "fixing" the original classes on the spot and keep the code-base smaller and more manageable. That leads to greater programmer efficiency.

Unit Testing

Dynamic languages walk hand-in-hand with unit testing. We will take a closer look at unit testing in the next lesson but let's just say that automated unit testing will help detecting a bug at the moment it is introduced in the code.

I wouldn't try tell you that unit testing is very popular in JavaScript, but in other dynamic languages like Ruby and Python it's common practice in enterprise-quality software. JavaScript is in a way still discovering the importance of unit testing.

Private members

Up to now all the properties and methods that we have been adding to our objects are accessible from any code that interacts with the object. In other object oriented programming languages it's often possible to define some of the properties and methods as private to the object itself. This private members are only accessible by code in the object itself but not from code that simply uses the object.

JavaScript, at the current version, does not support private members as a language feature. But not all is lost. With a clever little trick we can create objects with private members.

Code Sample: ObjectOrientedJS/Demos/private-members.html

---- Code Omitted ----
var Cycler = function () { this.values = arguments; //private members var current = 0; var itemCount = arguments.length; var that = this; var moveIndex = function () { current = (current + 1) % itemCount; } //public members this.next = function () { moveIndex(); return that.values[current]; }; }; var WEEKDAYS = new Cycler('Mon', 'Tue', 'Wed', 'Thu', 'Fri');
---- Code Omitted ----

The trick is related to JavaScript's variable scoping rules. Local variables inside the constructor function are visible inside the constructor, including in the functions defined inside the constructor, like moveIndex() and next(). Even moveIndex() is declared as a local variable.

Object Oriented Aspects of JavaScript Conclusion

In this lesson we started to see how JavaScript implements basic object creation. We also saw how flexible JavaScript is that we can even modify existing objects not created by us. We're only scratching the surface on the dynamism provided by this language.

In the next lesson we will explore JavaScript objects even further, when we explain how inheritance is implemented using protype objects.

To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Use of http://www.learn-javascript-tutorial.com (Website) implies agreement to the following:

Copyright Information

All pages and graphics on Website are the property of Webucator, Inc. unless otherwise specified.

None of the content on Website may be redistributed or reproduced in any way, shape, or form without written permission from Webucator, Inc.

No Printing or saving of pages or content on Website

This content may not be printed or saved. It is for online use only.


Linking to Website

You may link to any of the pages on Website; however, you may not include the content in a frame or iframe without written permission from Webucator, Inc.


Warranties

Website is provided without warranty of any kind. There are no guarantees that use of the site will not be subject to interruptions. All direct or indirect risk related to use of the site is borne entirely by the user. All code and explanations provided on this site are provided without warranties to correctness, performance, fitness, merchantability, and/or any other warranty (whether expressed or implied).


For individual private use only

You agree not to use this online manual to deliver or receive training. If you are delivering or attending a class that is making use of this online manual, you are in violation of our terms of service. Please report any abuse to courseware@webucator.com. If you would like to deliver or receive training using this manual, please fill out the form at http://www.webucator.com/Contact.cfm