2.2. ScopeThe next critical characteristic of a variable is its scope: whether it's local to a specific function or global to the entire JavaScript application. A variable with local scope is one that's defined, initialized, and used within a function; when the function terminates, the variable ceases to exist. A global variable, on the other hand, can be accessed anywhere within any JavaScript contained within a web pagewhether the JS is embedded directly in the page or imported through a JavaScript library. In Chapter 1, I mentioned that there is no special syntax necessary to specifically define a variable. A variable can be both created and instantiated in the same line of code, and it need not look any different from a typical assignment statement: num_value = 3.5; This is a better approach: var num_value = 3.5; The difference between the two is the use of the var keyword. Though not required, explicitly defining a variable using the var keyword is strongly recommended; doing so with local variables helps prevent collision between local and global variables of the same name. If a variable is explicitly defined in a function, its scope is restricted to the function, and any reference to that variable within the function is understood by both developer and JavaScript engine to be that local variable. With the growing popularity of larger, more complex JS libraries, using var prevents the unexpected side effects created by using what you think is a local variable, only to find out it's global in scope. To illustrate this type of side effect and the importance of explicitly declaring variables, Example 2-1 demonstrates a web page with separate blocks of JavaScript, each accessing the same variable, message. The page includes two external JavaScript files, both of which also set the same variable: one, globally, outside the function that uses it; the other, locally, within the function. None of the examples use the var keyword to expressly define the variable. Example 2-1. The dangers of global variables and not declaring local variables explicitly
When the page initially loads, the JS in the head element is processed first, and the message variable is set to a string with the words, I'm in the page. The variable is then output to a dialog window from a function, testScope, which is called when the page finishes loading. Before printing the message, though, the function also concatenates (adds to the end) the string, called in testScope( ) to the original text. Later in the web page, another block of JavaScript also accesses message and concatenates its own text to the message string: embedded in page. The modified string is then printed out to the web page. The page also imports two JavaScript external files. The first, global.js, concatenates its own string, globally in globalPrint, to the message. The source file also has a function, globalPrint, which opens a dialog and publishes the message: message += " globally in globalPrint"; function globalPrint( ) { alert(message); } The last component of this application, the JavaScript source file global2.js, has a function, global2Print, and also modifies message, this time by adding "also accessed in global2Print": function global2Print( ) { message += " also accessed in global2Print"; alert(message); } When the web page is first opened, message is initially set in the first script block, and then modified and printed out in the second script block as the page loads. The message at this point is: I'm in the page embedded in the page Nothing too surprising here. Via the onload event in the body tag, testScope is called as soon as the page is loaded. This function modifies the message string, and pops up a window with: I'm in the page embedded in the page called in testScope( ) Clicking the OK button closes the window, and the next function listed in onload is called: global2Print in global2.js. This function adds "Hi, you were here", resulting in a dialog with the following: I'm in a page embedded in page called in testScope( ) also accessed in global2Print The message is getting long and has been modified in multiple JS blocks across two files, but it's not finished yet. The last function called from the onload event is globalPrint in global.js. The JavaScript in this file modifies the string message, and globalPrint outputs it via an alert. The resulting text is: I'm in a page embedded in a page called in testScope( ) also accessed in global2Print Now, this is when what's happening with message may begin to get confusing. As you can see, the message didn't change between calling the function in global2.js and calling the function in global.js, but JavaScript in both files modified the string. The discrepancy in the result is that global2.js modified the string directly within the function before printing, treating it as a local variable, while global.js modified it outside the function, treating it as if it were global. When the JavaScript source file is loaded, the JavaScript is processed as the page is loaded, and the message set globally is lost as soon as the JavaScript in the head element of the web page body is processed. This script block treats message as a local variable and sets, rather than modifies, the message variable's contentsoverwriting the value set in the global variable of the same name. After a few more directs and redirects, and mixing global and local access of a variable with nothing to differentiate the two, you'll be surprised at the result at some point no matter how carefully you follow the variable. Guaranteed.
What's the moral of all of this, then? Well, there are two, really. The first is to be especially careful when using global variables. Some would say that with JavaScript's ability to create objects and attach properties, as well as pass values as function parameters, you shouldn't use global variables. However, global variables can be very handy: they can keep running counts or hold timers, or any value necessary to more than one function. Still, if you use large JavaScript libraries, no matter how careful you are, a global variable of the same name as the one you're using in your library can happen. When it does, you're going to get unexpected side effects.
If using extreme caution with global variables is one of the morals learned in this section, what's the second? It's this: always explicitly define a local variable using the var keyword. If you don't, it's treated as a global variablepure and simple.
|