Functions in JavaScript are lexically rather than dynamically scoped. This means that they run in the scope in which they are defined, not the scope from which they are executed. Prior to JavaScript 1.2, functions could be defined only in the global scope, and lexical scoping was not much of an issue: all functions were executed in the same global scope (with the call object of the function chained to that global scope).
In JavaScript 1.2 and later, however, functions can be defined anywhere, and tricky issues of scope arise. For example, consider a function g defined within a function f. g is always executed in the scope of f. Its scope chain includes three objects: its own call object, the call object of f( ), and the global object. Nested functions are perfectly understandable when they are invoked in the same lexical scope in which they are defined. For example, the following code does not do anything particularly surprising:
var x = "global"; function f( ) { var x = "local"; function g( ) { alert(x); } g( ); } f( ); // Calling this function displays "local"
In JavaScript, however, functions are data just like any other value, so they can be returned from functions, assigned to object properties, stored in arrays, and so on. This does not cause anything particularly surprising either, except when nested functions are involved. Consider the following code, which includes a function that returns a nested function. Each time it is called, it returns a function. The JavaScript code of the returned function is always the same, but the scope in which it is created differs slightly on each invocation, because the values of the arguments to the outer function differ on each invocation. If we save the returned functions in an array and then invoke each one, we'll see that each returns a different value. Since each function consists of identical JavaScript code and each is invoked from exactly the same scope, the only factor that could be causing the differing return values is the scope in which the functions were defined:
// This function returns a function each time it is called // The scope in which the function is defined differs for each call function makefunc(x) { return function( ) { return x; } } // Call makefunc( ) several times, and save the results in an array: var a = [makefunc(0), makefunc(1), makefunc(2)]; // Now call these functions and display their values. // Although the body of each function is the same, the scope is // different, and each call returns a different value: alert(a[0]( )); // Displays 0 alert(a[1]( )); // Displays 1 alert(a[2]( )); // Displays 2
The results of this code may be surprising. Still, they are the results expected from a strict application of the lexical scoping rule: a function is executed in the scope in which it was defined. That scope includes the state of local variables and arguments. Even though local variables and function arguments are transient, their state is frozen and becomes part of the lexical scope of any functions defined while they are in effect. In order to make lexical scoping work with nested functions, a JavaScript implementation must use a closure, which can be thought of as a combination of a function definition and the scope chain that was in effect when the function was defined.
Copyright © 2003 O'Reilly & Associates. All rights reserved.