Scope
Scope is a programming concept that exists to reduce the amount of variable and function collisions in your code. It controls how far information can travel throughout your JavaScript document. Earlier on, we briefly mentioned global variables. “Global” is a type of scope; the global scope for a variable means that the variable can be accessed and used anywhere in the document. Global variables are generally a bad thing, especially in larger files where naming collisions are more likely. Try to keep things out of the global scope if possible. Listing 6.3 shows how to declare a basic anonymous function and keep variables out of the global scope.
Listing 6.3. Defining an Anonymous Function
/* set up your anonymous function */ (function () { /* define a variable inside the function */ var greeting = "Hello Tim"; /* access the variable inside the function */ alert("in scope: " + greeting); })(); // end anonymous function
For the most part, you will be dealing in function-level scope. This means that any variable defined inside a function cannot be used outside that function. This is a great benefit of using anonymous functions. If you wrap a code block in an anonymous function, the contents of that function, which would normally default to the global scope, will now be contained within the scope of that anonymous function.
Listing 6.3.1 defines a variable inside an anonymous function, alerts the variable, and then tries to alert the variable again, outside the function (it won’t end well).
Listing 6.3.1. Showing Scope Inside an Anonymous Function
/* set up your anonymous function */ (function () { /* define a variable inside the function */ var greeting = "Hello Tim"; /* access the variable inside the function */ alert("in scope: " + greeting); })(); // end anonymous function /* try and access that variable outside the function scope */ alert("out of scope: " + typeof(greeting)); // alerts "undefined"
As you can see, the variable alert is undefined, even though you can see it’s clearly defined within the anonymous function. This is because the function scope will not allow the variable to leave the function.
Calling a Function with a Function
When you have a function that calls another function, the second function is referred to as a callback. The callback function is defined as a normal function with all the others but is executed inside another function. They’re a little different because instead of you having to do something to execute the function, another function does something. It’s like having robots that are built by other robots—total madness, I know.
Callback functions are a great way to separate out the levels of functionality in your code and make parts more reusable. Often you will see callback functions passed as arguments to other functions. We’ll get more into that in the next chapter when we talk about JavaScript events, and they’re especially important when dealing with server communications like Ajax. Listing 6.3.2 shows our sayHello() function being defined and then called inside the anonymous function. In this case, sayHello() is a callback function (calling it twice).
Listing 6.3.2. Using a Callback Function
function sayHello(message) { alert(message); } (function (){ var greeting = "Welcome", exitStatement = "ok, please leave."; sayHello(greeting); sayHello(exitStatement); })();
Returning Data
Every function you create will not result in a direct output. Up to this point you’ve been creating functions that do something tangible, usually alerting a piece of data into the browser. You won’t always want to do that, though; from time to time you will want to create a function that returns information for another function to use. This will make your functions a little smaller, and if the function that gathers information is general enough, you can reuse it to pass the same (or different) information into multiple functions.
Being able to return data and pass it into another function is a powerful feature of JavaScript.
Returning a Single Value
Going back to the sayHello() function that was defined in Listing 6.2, we’re going to remove the alert() action that was previously being executed when the function was called, and we’ll replace it with a return statement. This is depicted in Listing 6.3.3.
Listing 6.3.3. Returning Data with a Function
function sayHello(message){ return message + "!"; // add some emotion too }
You’ll probably notice that the sayHello() function doesn’t do anything in the browser anymore. That’s a good thing (unless you’re getting an error—that’s a bad thing). It means the function is now returning the data but it’s just sitting there waiting to be used by another function.
Returning Multiple Values
Sometimes returning a single value isn’t enough for what you’re trying to accomplish. In that case you can return multiple values and pass them in an array format to other functions. Remember how I mentioned that arrays are really important? They creep up a lot when dealing in data storage and flow in JavaScript. In Listing 6.3.4 you can see the sayHello() function taking two arguments. Those arguments get changed slightly and are resaved to variables; then they are returned in an array format to be accessed later.
Listing 6.3.4. Returning Multiple Data Values with a Function
function sayHello(greeting, exitStatement){ /* add some passion to these dry arguments */ var newGreeting = greeting + "!", newExitStatement = exitStatement + "!!"; /* return the arguments in an array */ return [newGreeting, newExitStatement]; }
Passing Returned Values to Another Function
Now that you’re returning variables, the next step is to pass those variables into another function so they can actually be used. Listing 6.3.5 shows the sayHello() function from Listing 6.3.1 returning an array of information and a new function called startle(), taking two arguments, passing them through the original sayHello() function, and alerting the results.
Listing 6.3.5. Using Returned Function Values Passed into Another Function
function sayHello(greeting, exitStatement){ /* add some passion to these dry arguments */ var newGreeting = greeting + "!", newExitStatement = exitStatement + "!!"; /* return the arguments in an array */ return [newGreeting, newExitStatement]; } function startle(polite, rude){ /* call the sayHello function, with arguments and same each response to a variable */ var greeting = sayHello(polite, rude)[0], exit = sayHello(polite, rude)[1]; /* alert the variables that have been passed through each function */ alert(greeting + " -- " + exit); } /* call the function with our arguments defined */ startle("thank you", "you stink");
A Function as a Method
Just as you can group variables and data into objects, you can also do it with functions. When you group functions into objects, they’re not called functions anymore; they’re called “methods.”
When I first started out with JavaScript, I came in from a design background rather than as a developer. This meant that I wasn’t familiar with common programming terms such as object, function, method, loop, and so on. I quickly learned what a function was and how to work with them through a lot of Googling. But I would hear people talk about the alert() method and other methods native to JavaScript, and I wouldn’t really get it because they look the same as functions. Why isn’t it the “alert function”? I had no idea. This comes up a lot when you’re dealing with JavaScript libraries as well (we get into that later in the book); everything is a method and nothing is a function, even though they all look and act the same.
Here’s what’s going on. In Chapter 5, “Storing Data in JavaScript,” you learned about storing information in objects. I mentioned that you could also store functions in objects. When you do that, they’re called methods instead of functions, but they work the same way. It’s weird, I know, and it’s not even an important distinction while you’re coding. It’s more about organizing your functions in groups to make them easier to maintain. The alert() method lives inside a global object (you never see it), which is why it’s called a method.
Now that we’re past that ordeal, organizing your functions into meaningful objects can clean up a lot of your code, especially on larger projects where you need the code organization help to keep your sanity. Listing 6.4 should look a little familiar; it shows how to organize our two functions (sayHello and startle) inside an object called “addressBookMethods.” If we were building a large-scale application with many features, this would be a great way to section off the functionality meant only for the address book feature.
Listing 6.4. Grouping Similar Functions
var addressBookMethods = { sayHello: function(message){ return message; }, startle: function(){ alert(addressBookMethods.sayHello("hey there, called from a method")); } } /* call the function */ addressBookMethods.startle();
Calling a method is a little different from calling a function. You’ll notice in Listing 6.4 that instead of calling startle() by itself, you have to call addressBookMethods.startle(). This is because before you can access the method, you have to access the object and drill down to the method.
Performance Considerations
Nesting functions in objects has the same performance implications that we spoke of when nesting variables in objects. The deeper a function is nested inside an object (addressBookMethods), the more resources it takes to extract. This is another place in your code where you will have to balance performance with maintainability. We’re not talking a ton of time here—maybe a few milliseconds difference—but it can add up. Most of the time it won’t matter, but if you find yourself needing a performance boost, function objects would be a place to look for a bottleneck. I probably wouldn’t go more than a few levels deep when creating these objects. Listing 6.4 goes only one level deep, which is a nice balance between performance and maintainability.