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
.
<script> function User(forename, username, password) { this.forename = forename this.username = username this.password = password this.showUser = function() { document.write("Forename: " + this.forename + "<br />") document.write("Username: " + this.username + "<br />") document.write("Password: " + this.password + "<br />") } } </script>
The function is different from other functions we’ve seen so far in two ways:
It refers to an object named this
. When the program creates an instance
of User
by running this function,
this
refers to the instance being
created. The same function can be called over and over with
different arguments and will create a new User
each time, with different values for
the properties forename
and so
on.
A new function named showUser
is created within the function.
The syntax shown here is new and rather complicated, but its purpose
is to tie showUser
to the
User
class. Thus, showUser
comes into being as a method of
the User
class.
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.
<script> function User(forename, username, password) { this.forename = forename this.username = username this.password = password this.showUser = showUser } function showUser() { document.write("Forename: " + this.forename + "<br />") document.write("Username: " + this.username + "<br />") document.write("Password: " + this.password + "<br />") } </script>
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.
<script> function User(forename, username, password) { this.forename = forename this.username = username this.password = password User.prototype.showUser = function() { document.write("Forename: " + this.forename + "<br />") document.write("Username: " + this.username + "<br />") document.write("Password: " + this.password + "<br />") } } </script>
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.
When reading about PHP objects, you learned that classes can
have static properties and methods as well as properties and methods
associated with a particular instance of a class. JavaScript also
supports static properties and methods, which you can conveniently
store and retrieve from the class’s prototype
. Thus, the following statements
set and read a static string from User
:
User.prototype.greeting = "Hello" document.write(User.prototype.greeting)
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.