JavaScript Objects

A JavaScript object is a step up from a variable, which can contain only one value at a time, in that objects can contain multiple values and even functions. An object groups data together with the functions needed to manipulate it.

When creating a script to use objects, you need to design a composite of data and code called a class. Each new object based on this class is called an instance (or occurrence) of that class. As you’ve already seen, the data associated with an object are called its properties, while the functions it uses are called methods.

Let’s look at how to declare the class for an object called User that will contain details about the current user. To create the class, just write a function named after the class. This function can accept arguments (I’ll show later how it’s invoked) and can create properties and methods for the objects in that class. The function is called a constructor.

Example 15-5 shows a constructor for the class User with three properties: forename, username, and password. The class also defines the method showUser.

The function is different from other functions we’ve seen so far in two ways:

The naming convention I have used is to keep all properties in lowercase and to use at least one uppercase character in method names, following the bumpyCaps convention mentioned earlier in the chapter.

Example 15-5 follows the recommended way to write a class constructor, which is to include methods in the constructor function. However, you can also refer to functions defined outside the constructor, as in Example 15-6.

I show you this form because you are certain to encounter it when perusing other programmers’ code.

To create an instance of the class User, you can use a statement such as the following:

details = new User("Wolfgang", "w.a.mozart", "composer")

Or you can create an empty object, like this:

details = new User()

and then populate it later, like this:

details.forename = "Wolfgang"
details.username = "w.a.mozart"
details.password = "composer"

You can also add new properties to an object, like this:

details.greeting = "Hello"

You can verify that adding such new properties works with the following statement:

document.write(details.greeting)

To access an object, you can refer to its properties, as in the following two unrelated example statements:

name = details.forename
if (details.username == "Admin") loginAsAdmin()

So, to access the showUser method of an object of class User, you would use the following syntax, in which the object details has already been created and populated with data:

details.showUser()

Assuming the data supplied earlier, this code would display:

Forename: Wolfgang
Username: w.a.mozart
Password: composer

The prototype keyword can save you a lot of memory. In the User class, every instance will contain the three properties and the method. Therefore, if you have 1,000 of these objects in memory, the method showUser will be replicated 1,000 times. However, because the method is identical in every case, you can specify that new objects should refer to a single instance of the method instead of creating a copy of it. So, instead of using the following in a class constructor:

this.showUser = function()

you could replace it with this:

User.prototype.showUser = function()

Example 15-7 shows what the new constructor would look like.

This works because all functions have a prototype property, designed to hold properties and methods that are not replicated in any objects created from a class. Instead, they are passed to its objects by reference.

This means that you can add a prototype property or method at any time and all objects (even those already created) will inherit it, as the following statements illustrate:

User.prototype.greeting = "Hello"
document.write(details.greeting)

The first statement adds the prototype property of greeting with a value of "Hello" to the class User. In the second line, the object details, which has already been created, correctly displays this new property.

You can also add to or modify methods in a class, as the following statements illustrate:

User.prototype.showUser = function() { document.write("Name " +
this.forename + " User " + this.username + " Pass " + this.password) }
details.showUser()

You might add these lines to your script in a conditional statement (such as if), so they run if user activities cause you to decide you need a different showUser method. After these lines run, even if the object details has been created already, further calls to details.showUser will run the new function. The old definition of showUser has been erased.

The prototype keyword even lets you add functionality to a built-in object. For example, suppose that you would like to add the ability to replace all spaces in a string with nonbreaking spaces in order to prevent it from wrapping around. This can be done by adding a prototype method to JavaScript’s default String object definition, like this:

String.prototype.nbsp =
    function() { return this.replace(/ /g, ' ') }

Here, the replace method is used with a regular expression (see Chapter 16) to find and replace all single spaces with the string “ ”. If you then enter the following command:

document.write("The quick brown fox".nbsp())

it will output the string “The quick brown fox”. And here’s a method you can add that will trim leading and trailing spaces from a string (once again using a regular expression):

String.prototype.trim =
    function() { return this.replace(/^\s+|\s+$/g, '') }

If you issue the following statement, the output will be the string “Please trim me” (with the leading and trailing spaces removed):

document.write("  Please trim me    ".trim())

Breaking down the expression into its component parts, the two / characters mark the start and end of the expression, and the final g specifies a global search. Inside the expression, the ^\s+ part searches for one or more whitespace characters appearing at the start of the search string, while the \s+$ part searches for one or more whitespace characters at the end of the search string. The | character in the middle acts to separate the alternatives.

The result is that when either of these expressions matches, the match is replaced with the empty string, thus returning a trimmed version of the string without any leading or trailing whitespace.