MSc-IT Study Material
June 2010 Edition

Computer Science Department, University of Cape Town

Objects as associative arrays

While it is clear that arrays are JavaScript objects, JavaScript also allows objects and their properties to be accessed as if they were arrays. Normal JavaScript arrays index their values in order using integer indices. When treating JavaScript objects as arrays, Strings are used as indices to associate each property value with the object name. Arrays of this kind are called "associative arrays".

To refer to an object property using array notation, simply pass the property name as a String to the array square brackets applied to the object, as follows:

        objectName["propertyName" ]
      

An example of the use of associative array notation for accessing object properties is illustrated in the following code:

        var object1 = new Object;

        object1.name = "Joanne";
        object1.age = 27;
        object1.nationality = "British";

        document.write(<p> property name: " + object1["name"] );
        document.write("<p> property age: " + object1["age" ] );
        document.write("<p> property nationality: " + object1["nationality" ] );

        var myArray = new Array(5);

        document.write("<p> array length: " + myArray["length" ] );
     

The browser output for this code is as follows:

It is important to note that although the square bracket indexing is used with associate arrays, it is objects that are being processed, and that these objects must be created with constructor functions (and the new keyword), or with object literals — associative arrays are not created using the built-in Array object.

Enumerating associative arrays with FOR/IN loop statements

One form of loop statement created for the processing of associative arrays is the FOR/IN statement. This statement allows the processing of all user-defined properties of arrays (and all inherited user-defined properties).

An example of the FOR/IN statement is:

        var object1 = new Object;

        object1.x = 29;
        object1.y = 6;
        object1.z = 55;

        for( var property in object1)
        {
          document.write("p>the value of property " + property + " is " + object1[ property ] );
        }
 

The output of this code in Internet Explorer is as follows:

Note

It is important to note that the developers of JavaScript did not prescribe the order in which the FOR/IN statement processes object properties. Therefore the output of such statements may vary from browser to browser, and the ordering cannot be relied upon to always be the same.

If the order is important, either more sophisticated programming is required, or the code must be tested on the specific browsers the JavaScript needs to run on — along with a clear warning that the code may not work on other browsers. Obviously, it is best to use this statement in such a way that the order in which properties are processed does not matter.

The real usefulness of the FOR/IN statement is that it allows the iterative processing of all properties of an object, in the same way that a numeric loop variable can be used to iteratively process all elements of a normal array. This technique is mainly useful where

  • The order of the properties either does not matter, or can be resolved

  • All (or at least most) properties of an object need to be processed in the same way

  • There are a large number of properties, so that a FOR/IN statement saves both programme statements and helps avoid the chance of forgetting to process an object property

Using FOR/IN for numerically index arrays

FOR/IN statements can be used to enumerate normal, numerically index arrays. For example, consider the code:

        var object1 = new Array( 3 );

        object1[0] = 4;
        object1[1] = 11;
        object1[2] = 22;

        for( var index in object1)
        {
          document.write("<p>the value of index " + index + " is " + object1[ index ] );
        }
      

The above code produces the following output on a browser:

However, as with any use of FOR/IN, there is no guarantee that the elements will be retrieved in the numerical index order. To ensure that elements are enumerated in numerical sequence (starting at 0), use a loop such as the following:

        var object1 = new Array( 3 );

        object1[0] = 4;
        object1[1] = 11;
        object1[2] = 22;

        var index = 0;

        while( index < object1.length )
        {
          document.write("<p>the value of index " + index + " is " + object1[ index ] );

          index++;
        }
      

Note

You may wish to read about the more elegant FOR loop statement for simple numerical loops such as the one above.

Activities and further work

Activity 6 — Using Arrays of Images to improve "Decades Music Shop"

The company "Decades Music Shop" has set up an on-line music ordering website. The site looks as follows:

The Webmaster who set up the site has made this a visually interactive home page by arranging the four images in a table. The table has three rows for "The 70s", "The 80s" and "The 90s" and the first column is an image spanning all 3 rows. The same page, with the table border set to 1 is as follows:

The images for "The 70s", "The 80s" and "The 90s" change when the mouse is over them — i.e. they have been made rollover images via the onMouseOver and onMouseOut event attributes of the IMG tag. The three screenshots below show how the page changes when the mouse is over each of these images in turn:

The current music shop Webmaster does not know about Arrays, and so has implemented a separate JavaScript function for each images onMouseOver and onMouseOut event — i.e. the Webmaster has had to create two functions for each image. The functions for the 70s image are as follows:

    function select70s()
    {
      document.the70s.src = "the70s_selected.gif";
      document.textArea.src = "the70s_selected_TEXT.gif";
    }

    function deselect70s()
    {
      document.the70s.src = "the70s_UNselected.gif";
      document.textArea.src = "UNselected_TEXT.gif";
    }
   

The HTML table is created with the following code:

    <TABLE CELLSPACING=0 CELLPADDING=0 WIDTH=202 BORDER=0>

    <TR>
        <TD rowspan = 3>
        <IMG NAME="textArea" SRC="UNselected_TEXT.gif">
        </TD>

        <TD>
        <A HREF="the70s.html" onMouseOver="select70s();"
        onMouseOut="deselect70s();">
        <IMG NAME="the70s" SRC="the70s_UNselected.gif" BORDER=0></A>
        </TD>
    </TR>

    <TR>
        <TD>
        <A HREF="the80s.html" onMouseOver="select80s();"
        onMouseOut="deselect80s();">
        <IMG NAME="the80s" SRC="the80s_UNselected.gif" BORDER=0></A>
        </TD>
    </TR>

    <TR>
        <TD>
        <A HREF="the90s.html" onMouseOver="select90s();"
        onMouseOut="deselect90s();">
        <IMG NAME="the90s" SRC="the90s_UNselected.gif" BORDER=0></A>
        </TD>
    </TR>

    </TABLE>
	

Notice how the 70s image onMouseOver and onMouseOut event attributes call the two functions listed above.

Your task is to reduce the number of required functions from six to two. You are to create a single function called selectImage() and another called deselectImage(), which are passed the number of the image to select or deselect. Refer to the 70s image as number 0, the 80s image as number 1 and the 90s image as number 2.

You are to achieve this reduction of functions by creating three arrays, called selectedImages, deselectedImages, and imageNames. They contain Strings holding the name of the relevant image as stated in the HTML IMG NAME="..." tag. Each array should contain an Image object whose src property has been set to the appropriate image.

changing a document image when referring to an array can be done in a way similar to this:

	  document.the80s.src = selectedImages[1].src;
	

If your function calls its index argument imageNumber, you can create a String that contains the statement you wish executed, and then use the eval() function to execute that String:

    var imageStatement = "document." + imageNames[imageNumber ];

    imageStatement += ".src = selectedImages[" + imageNumber + "].src";

    eval( imageStatement );
	

So if imageNames[ imageNumber ] contains "the80s" and then the variable imageStatement will contain precisely the statement you wish to execute!

You can find a discussion of this activity at the end of the unit.

Activity 7 — Using associative arrays of images to improve "Decades Music Shop"

Associative arrays offer a way to associate names rather than numbers with array elements.

Your task is to make the music_arrays.html more meaningful by replacing your arrays of images with objects that have a property for each image. This means that the need for an array of image names is removed, the need for an eval statement is removed, and the argument to the select and deselect functions becomes a more meaningful String, such as "the70s", or whatever IMG NAME has been set to.

HINT 1: Create an associative array (object) with images as properties.

Replace your array creation statements with object creation. Then replace array element assignment statements with object property statements:

    var selected = new Object;
    selected.the70s = new Image();
    selected.the70s.src = "the70s_selected.gif";
   

Thus we now have an object (i.e. an associative array) named selected, that can have its Image properties accessed via the property name, e.g.:

    selected["the70s"].src;
   

HINT 2: Pass the property name as an argument from onMouseOver and onMouseOut

Replace your HTML IMG lines with something similar to the following:

    <A HREF="the70s.html" onMouseOver="selectImage('the70s');" 
     onMouseOut="deselectImage('the70s');">

    <IMG NAME="the70s" SRC="the70s_UNselected.gif" BORDER=0>

    </A>
	

(remember, since you cannot have double quotes within double quotes, you will need to mix double and single quotes for the onMouseOver JavaScript one-liner String, since you are passing a String argument as part of your JavaScript statement).

You can find a discussion of this activity at the end of the unit.

Activity 8 — Using user-defined object with methods to improve "Decades Music Shop"

The previous activity greatly reduced the amount of code, and improved the readability and meaningfulness of function calls.

However, at present we have three objects containing our images, and two isolated functions. We can tidy things up even further by encapsulating the select and deselect functions into user-defined, rollover image objects.

Create a constructor function called RolloverImage() as follows:

Where the instance properties are as follows:

Stringnamethe name of the image this RolloverImage object has been setup for, e.g. "the70s"
Imageselected the selected version of the IMG
Imageunselected the unselected version of the the IMG
ImageselectedTEXTthe selected version of the Text IMG
ImageunselectedTEXTthe unselected version of the TEXT IMG (in fact this is the same for all, but it is tidier to define it for each RolloverImage object)

Create the instance methods for the prototype — these should be the methods select() and deselect().

Create an object in which to store your RolloverImage objects, call this object rolloverImages.

Create, as properties of your object rolloverImages, three instances of RolloverImage named the70s, the80s and the90s. This lets you use statements such as:

        rolloverImages.the70s.select()
	

for onMouseOver events, and so on.

HINT: The HTML table entries should call object methods.

Your HTML table entries should look something like the following

<TD>

<A HREF="the70s.html" onMouseOver="rolloverImages.the70s.select()" onMouseOut="the70s.deselect()">
<IMG NAME="the70s" SRC="rolloverImages.the70s_UNselected.gif" BORDER=0>
</A>

</TD>
   

You can find a discussion of this activity at the end of the unit.