Objects as Simulation
Let's say you wanted to write a computerized catalog for a library. You'd have a lot of information to manage, both for each book and for the total collection. Certainly you'd need some arrays, since you'll be adding new books all the time, and won't want to keep individual variables for each one. Even for the books themselves, it would be good to have all the information on each book grouped together. For example, you'll want to store:
- Title
- Author
- Number of pages
- Call number
- Publication date
- Subjects
- Description
One way to keep these things collected would be to use an array, and use set indexes for each property. book[0] might be the title, for example, and book[1] would be the author, and so on. But this is pretty awkward: each time you need to get the title, you have to remember that the index for title is 0, which is easy to forget. Someone maintaining your code is not going to thank you for that!
What you really need is a kind of array where, instead of using numbers, you could just name the values inside. Luckily, you can! An object, in JavaScript, is a collection like an array, but with string indexes instead of numerical ones. You declare an object using curly braces instead of the square braces that we use for arrays, and then the syntax is basically identical:
var book = {};
book["author"] = "Frank Herbert";
book["title"] = "Dune";
book["pagecount"] = 567;
book["subjects"] = ["Science Fiction", "Ecology", "Doom"];
console.log(book["pagecount"]); //logs out 567;
In this case, we first define book as an empty object. Then we add properties to the object, specifying the key for each with a string in brackets instead of a number and setting it equal to the desired value. What we call "indexes" for arrays are called "keys" on objects, but they serve the same role: to serve as a lookup address for the value inside the list. For our book, the key "author" is set to "Frank Herbert", and so forth. Just as with arrays, we can put any value into an object, including other objects and (as in the subjects property) arrays.
Object keys that are quoted this way can be any possible string. If you stick to the rules for variable names, though, there's a much shorter and easier way to talk about object properties. Properties that start with a letter and contain only letters, numbers, and the $ or _ characters, can be referenced using a dot instead of the square brackets.
var book = {};
book.author = "Frank Herbert";
book.title = "Dune";
console.log(book.title + ", by " + book.author);
//logs "Dune, by Frank Herbert"
This should start to look a little familiar, because we've used a few objects so far. For example, whenever we want to print something out to the console, we've been using console.log(): the console variable is an object that contains a number of useful functions--not just log, but also error, warn, and info, each of which prints a different kind of message to the console. If you type "console." (with the dot) into your console, most browsers will bring up a list of all the properties in the object that you can browse through.
Other objects that are built-in to the browser include:
- window - contains all global variables, including ones you declare for yourself. All variables that you define from the console are actually properties on the window object, as though you're "inside" it.
- Math - contains a bunch of useful functions like round() and sqrt() for doing calculations, along with some handy values like Math.PI.
- document - the current page as loaded into the browser. We'll do more stuff with the document in a bit.
Creating our own objects, however, is the really exciting part. We can do so using the syntax above, where we create an empty object and add properties. But there's also a literal syntax for declaring objects in a single step, the same way that we declare other literal values like numbers, strings, and booleans by just writing them into our code. To define our book as an object literal, we'd do this:
var book = {
author: "Frank Herbert",
title: "Dune",
subjects: ["Science Fiction", "Ecology", "Doom"]
};
console.log(book.title); //prints "Dune"
This syntax is a bit like declaring an array. In that case, we could add the starting values to the array by just listing them with commas between each item. In this case, we also list the object's properties separated by commas, but we have to specify the key for each value first, separated from its value with a colon. Be careful not to add a trailing comma after the last key:value pair--in some browsers, that will cause an error.
Looping through Objects
Objects are useful because they let us simulate real world objects easily, but they can also serve as "named lists" in contrast to the "numbered lists" of arrays. For example, you might create an object containing all the people in a group, using their first names as the keys and their lunch orders as the values:
var group = {
"Thomas": "Chicken salad sandwich",
"Belle": "Cobb salad",
"Wallace": "Cheese pizza"
};
Now, if we want to list out everyone's lunch order it's easy to do, as long as we know how to write a for...in loop for objects.
for (var key in group) {
console.log(key + " ordered " + group[key]);
}
This loop looks different, and with good reason. After all, arrays are predictable: they (mostly) start at 0 and (mostly) index their contents on ascending numbers. For objects, though, the key could be any string, and we can hardly be expected to start at "a" and work our way up through "zzzzzzzzzzzzz" to find them.
Instead, when we write a for...in loop, JavaScript will loop through the object for us and assign each key (not each value) to the variable we provide. You can name that variable anything you want, as long as it's followed by in o, where "o" is the name of the object you want to loop through. I always find it useful to create a new variable with a predictable name, as above (where I've named it "key" for self-evident reasons).
Remember, however, that this is not the value. It's the key--the string name of the property that we can use to find the actual value with the square brackets syntax, just as our array loops used array[i] to find the actual value of each item in array. Unfortunately, you can't use the dot syntax inside a loop, because we don't know the actual name of the variable during each repetition. Only in the square brackets will the value of your key variable--and not its name--be used to look up a value from your object.
var movie = {
title: "Alien",
director: "Ridley Scott",
star: "Sigourney Weaver"
};
for (var prop in movie) {
console.log(prop); //"title", "director", "star"
//WRONG - this logs "undefined" because movie has no property named "prop"
console.log(movie.prop);
//RIGHT - this logs the value of each property inside movie
//equivalent of movie["value of prop"]
console.log(movie[prop]); //"Alien", "Ridley Scott", "Sigourney Weaver"
}
Construction with Objects
Using objects gives us a way to create strongly-related values, by combining them under a single variable. By combining them with arrays and other objects, we can write code that is readable, elegant, and efficient. Let's work through an example using some real-world data that uses all the skills we've picked up over the last few chapters: if/else, for loops, arrays, and objects.
var quakes = [
{
magnitude: 4.3,
location: "Seattle"
},
{
magnitude: 2.7,
location: "San Francisco"
},
{
magnitude: 5.4,
location: "Las Vegas"
}
{
magnitude: 1.8,
location: "Washington, DC"
}
];
The above code sets up an array full of earthquake data. There are only four quakes in our set, but there could easily be more. The advantage of using a loop over this information is that it would scale, whether it was four earthquakes or four thousand. Now, we're going to look through our data and find out where the largest quake took place. First, we'll set up a variable to contain the highest quake we've found so far. We'll go ahead and assign the first earthquake to the variable, since we need some starting values.
var highest = quakes[0];
Then we'll loop over the quakes array, checking each item in turn. If it's higher in magnitude than the quake stored in highest, we'll store it in highest instead and keep going.
for (var i = 0; i < quakes.length; i++) {
var current = quakes[i]; //get the current quake object
if (current.magnitude > highest.magnitude) { //check against highest
highest = current; //if higher, reassign highest
}
}
console.log("The biggest earthquake took place in " + highest.location);
Problems like this start to show how objects and loops can make many tough problems trivially solveable. Sure, it's easy to find the biggest quake when there are only three items. When there are many more, however, or the comparisons become more complicated, you'll be glad to let the computer handle this kind of manual labor.
Example Code
For this chapter's sample, we'll combine objects and arrays again to examine another interesting data set (for certain values of interesting). In the code below, we define an array containing a series of on-screen objects from a video game, and we're going to filter it down to only those that match certain criteria. For exapmle, we may only want the monsters, or power-ups. We might want to do this in order to determine if the player is in contact with anything that would hurt or help her, or to do collision detection for the level structure itself. Filtering down a large, diverse collection into smaller subsets is a common task in programming, from video games to word-processing.
Exercises and Practice Questions
- Pick three random objects from the room around you. Describe those as JavaScript objects. Try to pick at least one container-like object, which holds other things, and think about how to simulate its contents.
- Write a JavaScript snippet that counts the number of times that each letter occurs in a given piece of text, using an object to store the counts for each letter. To make this easier, it helps to remember that you can treat a string as an array in most browsers.
- In the earthquake example above, we found the highest magnitude of earthquake. Write another loop that will find the average magnitude of all the quake objects.