Can you call yourself a scripter yet? Probably—you already know your way around a lot of JavaScript. But who wants to be a scripter when you can be a programmer? It’s time to get serious and take it up a notch—it’s time you learn about functions and objects. They’re the key to writing code that is more powerful, better organized and more maintainable. They’re also heavily used across HTML5 JavaScript APIs, so the better you understand them the faster you can jump into a new API and start ruling with it. Strap in, this chapter is going to require your undivided attention...
You can already do a lot with JavaScript, let’s take a look at some of the things you know how to do:
So far, though, a lot of your knowledge is informal—sure, you can get an element out of the DOM and assign some new HTML to it, but if we asked you to explain exactly what document.getElementById
is technically, well, that might be a little more challenging. No worries; by the time you leave this chapter you’re going to have it down.
Now to get you there, we’re not going to start with a deep, technical analysis of getElementById
, no no, we’re going to do something a little more interesting: We’re going to extend JavaScript’s vocabulary and make it do some new things.
You’ve been using built-in functions, like alert
, or even Math.random
, but what if you wanted to add your own? Let’s say we wanted to write some code like this:
So how does this all work? What happens when we actually invoke a function? Here’s the 10,000-foot view:
Okay, first we need a function.
Let’s say you’ve just written your new bark
function, which has two parameters, dogName
and dogWeight
, and also a very impressive bit of code that returns a dog’s bark, depending on its weight of course.
Now let’s invoke it!
You know how to call a function already: just use its name and give it any arguments it needs. In this case we need two: a string with the dog’s name, and the dog’s weight, which is an integer.
Let’s make that call and see how this works:
And, let the body of the function do its work.
After we’ve assigned the value of each argument to its corresponding parameter in the function—like “Fido” to dogName
and the integer 50 to dogWeight
—then we’re ready to evaluate all the statements in the function body.
Statements are evaluated from top to bottom, just like all the other code you’ve been writing. What’s different is that we’re doing it in an environment where the parameter names dogName
and dogWeight
are assigned to the arguments you passed into the function.
Optionally, we can have return statements in the body...
... and that’s where we return a value back to the code that makes the call. Let’s see how that works:
Note
Remember, functions aren’t required to return a value. But in this case, the bark function does return a value.
If we could have another moment to talk...
We know, we know, by Chapter 4 you thought you’d be flying in an HTML5 jetpack by now, and we’ll get there. But before we do, you really need to understand the underpinnings of the HTML5 JavaScript APIs, and we’re going to do that in this chapter.
So what are these underpinnings? Think of the HTML5 JavaScript APIs as made up of objects, methods (otherwise known as functions) and properties. And so to really get in and master these APIs, you need to understand those things pretty well. Sure, you could try to get by without knowing them, but you’ll always be guessing your way around the APIs while failing to use them fully (not to mention making lots of mistakes and writing buggy code).
So we just wanted to drop you a note before you got too far into this chapter to tell you what we are up to. Here’s the great thing: by the end of this chapter you’re going to understand objects, functions, methods and a lot of other related things better than about 98% of JavaScript scripters out there. Seriously.
When you define a function you can define it with one or more parameters.
When you call a function, you call it with arguments:
So you’ll only define your parameters once, but you’ll probably call your functions with a lot of different arguments.
Note
You’d be amazed how many people get this wrong—even books get it wrong, so if you read it differently elsewhere, now you know better....
You define a function with parameters, you call a function with arguments.
You already know that you can declare a variable by using the var
keyword and a name anywhere in your script:
And you’ve seen that you can also declare variables inside a function:
If a variable is declared outside a function, it’s GLOBAL. If it’s declared inside a function, it’s LOCAL.
But what does it matter? Variables are variables, right? Well, where you declare your variables determines how visible they are to other parts of your code, and, later, understanding how these two kinds of variables operate will help you write more maintainable code (not to mention, help you understand the code of others).
Where you define your variables determines their scope; that is, where they are defined and where they aren’t, where they’re visible to your code and where they aren’t. Let’s look at an example of both locally and globally scoped variables—remember, the variables you define outside a function are globally scoped, and the function variables are locally scoped:
When you’re a variable, you work hard and life can be short. That is, unless you’re a global variable, but even with globals, life has its limits. But what determines the life of a variable? Think about it like this:
Globals live as long as the page. A global variable begins life when its JavaScript is loaded into the page. But, your global variable’s life ends when the page goes away. Even if you reload the same page, all your global variables are destroyed and then recreated in the newly loaded page.
Local variables typically disappear when your function ends. Local variables are created when your function is first called and live until the function returns (with a value or not). That said, you can take the values of your local variables and return them from the function before the variables meet their digital maker.
Note
We say “typically” because there are some advanced ways to retain locals a little longer, but we’re not going to worry about them now.
So, there really is NO escape from the page is there? If you’re a local variable, your life comes and goes quickly, and if you’re lucky enough to be a global, you’re good as long as that browser doesn’t reload the page.
But there just has to be a way to escape the page! We can find a way! Can’t we?
Here’s what that means: say you have a global variable beanCounter
and you then declare a function, like this:
When you do this, any references to beanCounter
within the function refer to the local variable and not the global. So we say the global variable is in the shadow of the local variable (in other words we can’t see it because the local version is in our way).
OK, you’ve used variables to store numbers, boolean values, strings, arrays, all kinds of things, but did we mention you can also assign a function to a variable? Check this out:
Well, not only did we fail to mention this little detail about functions before now, but we also weren’t totally honest when we told you about the anatomy of a function—as it turns out, you don’t even have to give your function a name. That’s right: your function can be anonymous. What the heck does that mean, and why would you want to do such a thing? First let’s see how you create a function without a name:
So what’s the big deal? Why is this useful? Well, the important thing isn’t so much that we can assign a function to a variable, that’s just our way of showing you that a function actually is a value. And you know you can store values in variables or arrays, you can pass them as arguments to functions, or as we’ll soon see, you can assign them to the properties of objects. But, rather than talking you through how anonymous functions are useful, let’s just look at one of the many ways using functions as values starts to get interesting:
Or we could get even fancier:
You might be starting to see that functions can do some useful things beyond just packaging up code for reuse; to give you a better idea of how to fully take advantage of functions, we’re going to take a look at objects and see how they fit into JavaScript, and then we’ll put it all together.
Well we thought we’d covered that already... but if it looks like we’ve picked you up and have driven you halfway around the city with the meter running (when we could have driven you straight downtown), well, then remember we’re about to start diving into the APIs that work with HTML5 in the next chapter. And, doing that is going to require that you really understand functions, objects and a few other related topics.
So hang in there—in fact you’re halfway there! And don’t forget, this is the chapter where you’re going from scripter to programmer, from an HTML/CSS jockey to someone who is capable of building real apps.
Ah, our favorite topic! Objects are going to take your JavaScript programming skills to the next level—they’re the key to managing complex code, to understanding the DOM, to organizing your data, and they’re even the fundamental way HTML5 JavaScript APIs are packaged up (and that’s just our short list!). That said, objects are a difficult topic, right? Hah! We’re going to just jump in head first and you’ll be using them in no time.
Here’s the secret to JavaScript objects: they’re just a collection of properties. Let’s take an example, say, a dog. A dog’s got properties:
Of course Fido would be the first to admit there’s a lot more to him than just a few properties, but for this example, those are going to be the ones we need to capture in software. Let’s think about those properties in terms of JavaScript data types:
So we’ve got an object with some properties; how do we create this using JavaScript? Here’s how:
Note
The Dot Operator.
The dot operator (.) gives you access to an object’s properties. In general it’s easier to read than the [“string”] notation:
fido.weight
is the size of fido.fido.breed
is the breed of fido.fido.name
is the name of fido.fido.loves
is an array containing fido’s interests.
Yes, you can add or delete properties at any time.
To add a property to an object you simply assign a new property a value, like this:
fido.age = 5;
and from that point on fido
will have a new property: age
.
Likewise, you can delete any property with the delete
keyword, like this:
delete fido.age;
When you delete a property, you’re not just deleting the value of the property, you’re deleting the property itself. In fact, if you use fido.age
after deleting it, it will evaluate to undefined
.
The delete
expression returns true
if the property was deleted successfully (or if you delete a property that doesn’t exist or if what you’re trying to delete isn’t a property of an object).
We’ve already talked a bit about how arguments are passed to functions—arguments are passed by value, so if we pass an integer, the corresponding function parameter gets a copy of the value of that integer for its use in the function. The same rules hold true for objects, however we’ve got to look a little more closely at what a variable holds when it is assigned to an object to know what this means.
When an object is assigned to a variable, that variable holds a reference to the object, not the object itself. Think of a reference as a pointer to the object.
So, when you call a function and pass it an object, you’re passing the object reference—not the object itself, just a “pointer” to it. A copy of the reference is passed into the parameter, which then points to the original object.
So, what does this all mean? Well, when you change a property of the object, you’re changing the property in the original object, not a copy, and so, you’ll see all the changes you make to an object within and outside of your function. Let’s step through an example using a loseWeight
function for dogs...
Let’s take a look at what’s going on when we pass fido
to loseWeight
and change the dog.weight
property.
Behind the Scenes
We’ve already had a small taste of mixing objects and functions. Let’s take this further by writing some code to tell us when the next showing of a movie is. Our function’s going to take a movie as an argument, and return a string containing the next time it plays, based on your current time.
Get the code on the previous page typed in and let’s give it a test run. You’ll see that the getNextShowing
function takes whatever movie it is handed and figures out the next showing time. Feel free to create some new movie objects of your own and give them a test drive too. We did, at our own local time of 12:30pm:
You didn’t think objects were just for storing numbers, strings and arrays did you? Objects are active, they can do things. Dogs don’t just sit there: they bark, run, play catch and a dog object should too! Given everything you’ve learned in this chapter, you’re all set to add behavior to your objects. Here’s how we do that:
When an object has a function in it, we say that object has a method.
To call a method on an object we use the object name along with the method using our dot notation, and supply any arguments needed.
Now that your knowledge of objects is expanding we can go back and improve the cinema code. We’ve already written a getNextShowing
function that takes a movie as an argument, but we could instead make this part of the movie object by making it a method. Let’s do that:
We actually can’t just throw the function in this object because getNextShowing
takes a movie argument, and what we really want is to call getNextShowing
like this:
Alright, so how do we fix this? We’ve got to remove the parameter from the getNextShowing
method definition, but then we need to do something with all the references to movie.showtimes
in the code because, once we remove the parameter, movie
will no longer exist as a variable. Let’s take a look...
We’ve taken the liberty of removing the movie
parameter, and all the references to it. Which leaves us with this code:
Alright, here’s the conundrum: we’ve got these references to the properties showtimes
and title
. Normally in a function we’re referencing a local variable, a global variable, or a parameter of the function, but showtimes
and title
are properties of the movie1
object. Well maybe this just works... it seems like JavaScript might be smart enough to figure this out?
Nope. It doesn’t work. Feel free to give it a test drive; JavaScript will tell you the showtimes and title variables are undefined. How can that be?
Okay, here’s the deal: these variables are properties of an object, but we aren’t telling JavaScript which object. You might say to yourself, “Well, obviously we mean THIS object, this one right here! How could there be any confusion about that?” And, yes, we want the properties of this very object. In fact, there’s a keyword in JavaScript named this
, and that is exactly how you tell JavaScript you mean this object we’re in.
Now, the situation is actually a little more complicated than it appears here, and we’re going to get to that in a second, but for now we’re going to add the this
keyword and get this code working.
Let’s add this
each place we specify a property, so that we’re telling JavaScript we want the property in this object:
Go ahead and type in the code above and also add the getNextShowing
function to your movie2
object (just copy and paste it in). Then make the changes below to your previous test code. After that give it a spin! Here’s what we got:
You have great instincts if you recognized that we are duplicating code when we copy getNextShowing
into more than one movie object. One of the aims of “object-oriented” programming is to maximize code reuse—here we’re not reusing any code, in fact we’re creating every object as a one-off, and our movie objects just happen to be the same by convention (and copying and pasting!). Not only is that a waste, it can be error prone.
There’s a much better way to do this using a constructor. What’s a constructor? It’s just a special function we’re going to write that can create objects for us, and make them all the same. Think of it like a little factory that takes the property values you want to set in your object, and then hands you back a nice new object with all the right properties and methods.
Let’s create a constructor...
Let’s make a constructor for dogs. We already know what we want our dog objects to look like: they have name, breed and weight properties, and they have a bark method. So what our constructor needs to do is take the property values as parameters and then hand us back a dog object all ready to bark. Here’s the code:
So let’s walk through this again to make sure we’ve got it. Dog is a constructor function and it takes a set of arguments, which just happen to be the initial values for the properties we want: name, breed and weight. Once it has those values, it assigns properties using the this
keyword. It also defines our bark method. The result of all this? The Dog constructor returns a new object. Let’s see how to actually use the constructor.
Now that we’ve got our factory built, we can use it to create some dogs. There’s only one thing we haven’t told you, which is that you need to call a constructor function in a special way by putting the keyword new
before the call. Here are some examples:
Let’s review what’s going on here one more time: we’re creating three different dog objects, each with its own properties, using the new
keyword with the Dog constructor that we created. The constructor returns a Dog object customized with the arguments we passed in.
Next, we call the bark
method on each one—notice that we’re sharing the same bark
method across all dogs, and when each dog barks, this
points to the dog object that made the call. So if we call the bark
method on fido
, then, in the bark method, this
is set to the fido object. Let’s look a little closer at how that happens.
Anytime we put this
in the code of a method it will be interpreted as a reference to the object the method was called on. So, if we call fido.bark
, then this
is going to reference fido
. Or, if we call it on our dog object tiny
then this
is going to reference tiny
within that method call. How does this
know which object it is representing? Let’s see:
Now that you’ve got a Movie constructor, it’s time to make some Movie objects! Go ahead and type in the Movie constructor function and then add the code below and take your constructor for a spin. We think you’ll agree this a much easier way to create objects.
Now, you might have started to notice...
...that objects are all around you. For instance, document
and window
are objects, as are the elements we get back from document.getElementById
. And, these are just a few of many objects we’ll be encountering—when we get to the HTML5 APIs, we’ll be seeing objects everywhere!
Let’s take a second look at some of the objects you’ve been using all along in this book:
When you’re writing code for the browser, the window
object is always going to be part of your life. The window object represents both the global environment for your JavaScript programs and the main window of your app, and as such, it contains many core properties and methods. Let’s take a look at it:
It may seem a little weird, but the window object acts as your global environment, so the names of any properties or methods from window are resolved even if you don’t prepend them with window
.
In addition, any global variables you define are also put into the window namespace, so you can reference them as window.myvariable
.
One thing we’ve used often so far in this book is a window.onload
event handler. By assigning a function to the window.onload
property, we can ensure our code isn’t run until the page is loaded and the DOM is completely set up. Now, there’s a lot going on in the window.onload
statement, so let’s have another look and it will all start to come together for you:
The document
object is another familar face; it’s the object we’ve been using to access the DOM. And, as you’ve just seen, it is actually a property of the window
object. Of course we haven’t used it like window.document
because we don’t need to. Let’s take a quick peek under the covers to see its more interesting properties and methods:
We promised in the begining of this chapter that you’d understand document.getElementById
by the end of the chapter. Well, you made it through functions, objects, and methods, and now you’re ready! Check it out:
What was a confusing looking string of syntax now has a lot more meaning, right? Now, that div
variable is also an object: an element object. Let’s take a closer look at that too.
We shouldn’t forget when we’re working with methods like getElementById
that the elements they return are also objects! Okay, you might not have realized this, but now that you know, you might be starting to think everything in JavaScript is an object, and, well, you’re pretty much right.
You’ve already seen some evidence of element properties, like the innerHTML
property; let’s look at some of the more notable properties and methods:
Get Head First HTML5 Programming now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.