JavaScript Objects

So far in this book, you've heard me mention objects quite often but I've never explained what they really are. To put it simply, an object is a collection of values and methods (which are, remember, functions associated with objects). It is perfectly permissible to have an object that is all values or all methods.

I mentioned something back in back in The Document Object Model that is very important to remember. To quote it completely, Always remember that a node must actually exist before you do anything with it. Nodes are a type of object, and what applies to them applies to all objects.

Object Creation

There are two ways to create an object: Either directly (which has its own shorthand method) or through a template.

Directly Creating An Object

Directly creating an object is actually quite easy. First, you create a variable (object), and say that it is a new object (You've done this for arrays).

Directly Creating An Object
var object = new Object();

Then we decide what properties it's supposed to have. Properties, of course, follow the object name with a period between the object name and property name (object.property). So, let's set the properties Num1, Num2, Num3, and String:

Adding Values To An Object
var object = new Object();
object.Num1 = 4;
object.Num2 = 5;
object.Num3 = 6;
object.String = "Something Else";

Now where have we seen THAT syntax before? Oh, yes, we used the data property of a text node.

So when we want to use these values, say to write to various elements, we can use their values like any other variable:

Using The Values Of An Object
var dds = document.getElementsByTagName("dd");
dds[0].firstChild.data = object.String;
dds[1].firstChild.data = object.Num1;
dds[2].firstChild.data = object.Num2;
dds[3].firstChild.data = object.Num3;

And the results are below:

Object Demonstration

Now, you might be thinking But object doesn't HAVE those properties. Actually, when you say, Property A of Object B has Value C, you're also saying Object B has Property A.

I'll show you, working with nodes.

Adding Properties To Element Nodes
var dd1 = document.getElementByID("dd_1");
var dd2 = document.getElementByID("dd_2");
var js = document.getElementByID("JS");
dd1.data = js.extra;
js.extra = "Extra Text";
dd2.data = js.extra;

Basically, in the last three lines, I set the text of the elements dd_1 and dd_2 (both dd elements, by the way) to the property extra of js, which is assigned a reference to a paragraph element node). Element nodes do not normally have the property extra, so for dd_1, (which is set to display that property's value before the property itself is set) it's non-existent, as shown by the word undefined. However, once set, that particular element node has the property extra, and so dd_2 has something to display:

Adding a property

Direct Creation Shorthand

Like arrays, there is a shorthand method of creating objects. Instead of using [], however, you use {}. Also, the syntax is slightly different, because you're not dealing with just values but the properties as well. So below is our object, as we created it earlier:

Normal Direct Creation
var object = new Object();
object.String = "Something Else";
object.Num1 = 4;
object.Num2 = 5;
object.Num3 = 6;

To use the shorthand method, you have to have a comma-separated list of properties and values. The property comes first, then a colon, then the value:

Shorthand Syntax
property:value

Well, where have we seen that syntax before? Oh, yes: CSS! This works on much the same principle.

In any case, the above code rewritten in shorthand would be:

Shorthand Creation
var object = {
String:"Something Else",
Num1:4,
Num2:5,
Num3:6
};

Watch out that you don't use semicolons in there, or things will get messy. Just like in arrays.

Creating An Object Through A Template

A template is a means to create multiple objects that are identical in properties, but not necessarily in values. These separate objects are known as instances, to create them is to instantiate the object. (In direct creation, the object is instantiated when you use var object = new Object() or when you use shorthand creation.)

The template itself is a function. These functions, however, do not return a value in the same way. Below, I've created a template with four variables (Num1, Num2, Num3 and String) called Template.

I know, how very imaginative.

A really important keyword in templates is this, and refers to this instance of the object.

I'll show you what I mean. Below is a template of an object:

The Template Of An Object
function Template(N1, N2, N3, String){
this.String = String;
this.Num1 = N1;
this.Num2 = N2;
this.Num3 = N3;
}

Note that the properties of this differ from the names of the variables. That is fine; but when you refer to the properties of an object created by Template, you would use Num1, Num2, and Num3 instead of N1, N2, and N3, since the previous group are specifically stated to be the actual property names.

We create a new instance of this object by calling the template and assigning its value to object below, and declaring it to be a new instance of the object Template. This is exactly like creating a new object (see above) or new array (as shown in previous chapters).

Creating A New Object Through A Template
var object = new Template(4, 5, 6, "Something else");

When used in such a manner, this means this instance of Num1 is equal to 4, this instance of Num2 is equal to 5, this instance of Num3 is equal to 5, and this instance of String is equal to Something else.

The advantage to using a template is that we can create as many instances of Template as we need. For example, I created a webpage that used a template to create a new object for every row of a table. Since that table was used to create a seven-month schedule, that template was instantiated more than two hundred times.

Below, I create an array containing two separate instances of it (though we could use two separate variables entirely).

Creating Two New Objects In An Array Through A Template
var objects = new Array(
new Template(4, 5, 6, "Something else"),
new Template(8, 10, 12, "Different Text")
)

Before I demonstrate more, I should tell you about a trick that's possible: when one refers to the property of an object, you usually have the object, a period, then the property. An example would be to refer to the length of the array objects:

The Normal Way To Refer To A Property
objects.length

But it is permissible to act as if the object were an array, and use the property name as a string in place of an index number. The code also refers to the property length:

A Property Name Used Like An Array Index
objects["length"]

This is important, because strings can be manipulated, as you will see in the code that assigns text to elements:

Taking Advantage Of The Alternative
function Text(r, c){return r.getElementsByTagName("td")[c].firstChild;}
var trs = document.getElementsByTagName("tr");
for(var l = 1; l < trs.length; l++){
Text(trs[l], 1).data = objects[l - 1].String
for(var l2 = 1; l2 <= 3; l2++){
Text(trs[l], (l2 + 1)).data = objects[l - 1]["Num" + l2]
}
}

As you know, three properties share the letters Num. Concatenating the strings with the properties' respective numbers allows me to assign their text to text nodes via a loop. Below, each row represents a different instance of the object Template, and you can see their different values:

Object Demonstration

Adding Methods

A method is a function associated with an object. Making a function a method is actually quite easy: treat the function name like a variable. First, we create the function Get_El, saying that its purpose is to get an element. It can get an element by ID or by tag name, and if the second value is undefined, then treat the first like an ID:

Create A Function To Use In Object
function Get_El(name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}

Just a note here: I didn't try to use num as a boolean in and of itself (Example: if(!num) ) since, in that condition, a value of 0 would also prove not false right along with undefined, but in the case of node lists, 0 refers to the first element in that list. Therefore, only an undefined value will cause it to treat the first value as an ID instead of an element name.

The next two samples deal with direct creation; the third deals with a template:

Adding The Method To The Object
Using Direct Creation
var object = new Object();
object.Num1 = 4;
object.Num2 = 5;
object.Num3 = 6;
object.String = "Something Else"
object.Get_El = Get_El;
Using Direct Creation Shorthand
var object = {
Num1:4,
Num2:5,
Num3:6,
String:"Something Else"
Get_El:Get_El
};
Using A Template
function Template(N1, N2, N3, String){
this.Num1 = N1;
this.Num2 = N2;
this.Num3 = N3;
this.String = String;
this.Get_El = Get_El;
}
var object = new Template();

Note that, in the template (which I left otherwise unaltered), I did not add a function variable to the function Template to deal with the new property Get_El. Since Get_El is a method, not a property, I don't need to; I pass values to it when I use Get_El through the object. In the following example, I show accessing Get_El through an object created via template, but I also display one minor redundancy:

Using A Method
Script In head
function Get_El(name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}
function Template(N1, N2, N3, String){
this.Num1 = N1;
this.Num2 = N2;
this.Num3 = N3;
this.String = String;
this.Get_El = Get_El;
}
var object = new Template();
Script In body
var object = new Template();
var fee_fie = object.Get_El("write_to");
var foe_fum = Get_El("get_from");
fee_fie.firstChild.data = foe_fum.firstChild.data;

The variable foe_fum accesses Get_El directly, rather than through object. Since Get_El is a global function, this works:

Setting text by method

If you want Get_El to be exclusive to Template so you don't have functions tripping over each other, nesting functions comes in handy.

Making An Object-Exclusive Method
function Template(N1, N2, N3, String){
this.Num1 = N1;
this.Num2 = N2;
this.Num3 = N3;
this.String = String;
this.Get_El = Get_El;
// Function Get_El is now exclusive to Template
function Get_El(name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}
}
var object = new Template();

A trick that can save some code is assigning the function itself to the property, rather than just the function's name:

Making A Method Exclusive To An Object (Variant)
function Template(N1, N2, N3, String){
this.Num1 = N1;
this.Num2 = N2;
this.Num3 = N3;
this.String = String;
this.Get_El = function (name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}
}
var object = new Template();

Now, if I try to access Get_El directly, I get an error saying it's undefined. It does not exist outside of the template or objects created thereby. As I said earlier, it's a good way to keep functions from tripping over each other.

Nesting Objects

It is quite possible (and common) to nest an object within an object. It's like nesting arrays or functions. To demonstrate this, I will place the number properties in their own object called Numbers:

Nesting Objects
Using Direct Creation
var object = new Object();
object.Numbers = new Object();
object.Numbers.Num1 = 4;
object.Numbers.Num2 = 5;
object.Numbers.Num3 = 6;
object.String = "Something Else"
object.Get_El = function (name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}
};
Using Direct Creation Shorthand
var object = {
Numbers:{
Num1:4,
Num2:5,
Num3:6
},
String:"Something Else"
Get_El:function (name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}
};
Using A Template
function Template(N1, N2, N3, String){
this.Numbers = new Numbers(N1, N2, N3);
this.String = String;
this.Get_El = function (name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}
 
function Numbers(Num1, Num2, Num3){
this.Num1 = Num1;
this.Num2 = Num2;
this.Num3 = Num3;
}
}
var object = new Template();

Note that, when I nest a template (in this case Numbers) inside another template (Template), I call it like any other template.

Combining the above methods, of course, is perfectly acceptable:

Combinations
Normal Direct Creation With Shorthand
var object = new Object();
object.Numbers = {
Num1:4,
Num2:5,
Num3:6
}
Shorthand Direct Using Template
var object = {
Numbers:new Numbers(4, 5, 6);
}
function Numbers(Num1, Num2, Num3){
this.Num1 = Num1;
this.Num2 = Num2;
this.Num3 = Num3;
}
Template Using Direct Creation
function Template(N1, N2, N3, String){
var Numbers = new Object();
Numbers.Num1 = N1;
Numbers.Num2 = N2;
Numbers.Num3 = N3;
 
this.Numbers = Numbers;
this.String = String;
this.Get_El = Get_El;
 
function Get_El(name, num){
var el;
if(num == undefined){
el = document.getElementById(name);
} else {
el = document.getElementsByTagName(name)[num];
}
return el;
}
}
var object = new Template();

Getting Object Info

Mostly we've been getting specific data from objects, but there are more ways of getting what you need than what I've showed you thus far.

For example, earlier in the chapter I mentioned that the property name can be used like an array's index, if the property's name is passed as a string. This might not seem like much, but there is an interesting application: you can specify the property you want using a variable.

Property Name In Variable
property = "Num1";
object[property];

This will come in handy when you want to get all the properties and values in an object, including the ones you don't know about.

Objects And Loops

You can use a loop to go through the properties of an object, one by one. However, you should know that the only loop that does so is the for-in loop; other loops need to know what properties the object has beforehand.

But do note that for-in loops have a challenge of their own. Take a look at the following code example:

Using A For-In Loop With An Object
var object = new Template(4, 5, 6, "Something else");
for(property in object){
}

The variable property, because it refers to the names of the properties of object, will always hold a string, never a number. Therefore, the following code will not work:

Using property As An Index
var object = new Template(4, 5, 6, "Something else");
for(property in object){
document.getElementsByTagName("tr")[property].getElementsByTagName("td")[0].firstChild.data = object[property];
}

A node list must use a numerical index, not a textual one. Therefore, a separate counter must be created to work with those. Adding in the counter and another line of code to display the actual value of property, we get:

Using A Counter As An Index
var object = new Template(4, 5, 6, "Something else");
var count = 1;
for(property in object){
document.getElementsByTagName("tr")[count].getElementsByTagName("td")[0].firstChild.data = property;
document.getElementsByTagName("tr")[count].getElementsByTagName("td")[1].firstChild.data = object[property];
count++;
}

This yields the following:

Using a for-in loop.