This blog post explains how variables are scoped in JavaScript. It points out two things that can cause problems if you are not aware of them: JavaScript variables are function-scoped and they can be captured in closures.

The scope of a variable defines where the variable is accessible. For example, if a variable is declared at the beginning of a function, it is accessible from within that function, but not from outside and usually dies when the function is finished. In this case, the function is the scope of the variable. When a scope is entered, a new environment is created that maps variable names to values. Scopes can be nested. A variable is accessible in its scope and in all scopes nested within that scope.

Pitfall 1: Variables are function-scoped. Most mainstream languages are block-scoped – new environments are created when entering a block and scopes are nested by nesting blocks. In contrast, JavaScript variables are function-scoped – new environments are only created when entering a function and scopes are nested by nesting functions. That means that even if you declare a variable inside a block such as the “then” block of an if statement, it is accessible everywhere in the surrounding function. The following code illustrates this.

var myvar = "global"; function f() { print(myvar); // (*) if (true) { var myvar = "local"; // (**) } print(myvar); }

> f(); undefined local > myvar global

myvar

var

var myvar = "local"

myvar

f()

var

As you can see, even the first access ofrefers to the local value of it (which is not yet assigned at (*)). The reason for this is that-declared variables arein JavaScript:is equivalent to declaringat the beginning ofand performing a simple assignment at location (**). Thus, it is a best practice in JavaScript to only useat the beginning of a function.

Pitfall 2: Closures. Scoping in JavaScript is static, it is determined by the nesting of syntactic constructs. To ensure static scoping, the environment is attached to values that access variables in the environment. An example of such a value is the returned function in the following code.

function f() { var x = "abc"; return function() { return x; } }

> var g = f(); > g() abc

x

x

function objMaker(color) { return { getColor: function() { return color; }, setColor: function(c) { color = c; } }; }

> var c = objMaker("blue"); > c.getColor() blue > c.setColor("green"); > c.getColor() green

color

objMaker()

color

objMaker()

Interaction:Variableiswithin the returned function, it cannot be resolved within it. By attaching the environment,can be resolved to the value we expect given static scoping. This pairing of a value with an environment is called a, because the free variables are closed over. JavaScript’s closures are very powerful. You can even use them to store the properties of an object, as demonstrated in the following code.Interaction:The value ofis stored in the environment that has been created when invoking. That environment has been attached to the returned value, which is whyis still accessible even afterhas terminated.

Inadvertently sharing an environment. Closures plus function-scoped variables lead to unexpected behavior. Given the following code.

function f() { var arr = [ "red", "green", "blue" ]; var result = []; for(var i=0; i<arr.length-1; i++) { var func = function() { return arr[i]; }; result.push(func); } return result; }

f

arr

i

"blue"

> f()[0]() blue

i

This function returns an array with two functions in it. Both of these functions can still access’s environment and thus. In fact, they share the same environment. But in that environment,has the value 2 and thus both functions returnwhen invoked:This is not what we wanted. In order for this to work, we need to make a snapshot of the indexbefore creating a function that uses it.

Creating new environments. In block-scoped programming languages the following would work.

function f() { var arr = [ "red", "green", "blue" ]; var result = []; for(var i=0; i<arr.length-1; i++) { // New environment for every iteration? Not in JS! var color = arr[i]; // NOT a fresh copy in JS! var func = function() { return color; }; result.push(func); } return result; }

color

f()

func

(function() { // open block // inside block }()); // close block

function

function f() { var arr = [ "red", "green", "blue" ]; var result = []; for(var i=0; i<arr.length-1; i++) { (function() { var color = arr[i]; // fresh copy var func = function() { return color; }; result.push(func); }()); } return result; }

> f()[0]() red

function f() { var arr = [ "red", "green", "blue" ]; var result = []; for(var i=0; i<arr.length-1; i++) { (function(color) { var func = function() { return color; }; result.push(func); }(arr[i])); } return result; }

JavaScript is function-scoped, sois handled as if it had been declared at the beginning ofand we don’t get a different environment for eachthat we return. Only functions can create environments, so we use a function to simulate an environment-creating block. This looks as follows:Because we immediately invoke the function, the behavior regarding the wrapped code is the same as for blocks: The code is executed right away. Why the parentheses around this construct? They are there to avoid a syntax error. If a statement starts with, a function declaration is expected. Thus, without the opening parenthesis, the parser complains about a missing name for the declaration. You might get the idea to provide such a name, but then the parser complains about the empty parentheses following the function declaration. In either case, the function would not be executed. Thus, we use the opening parenthesis to indicate that an expression will follow, a function expression. This is called an Immediately Invoked Function Expression (IIFE, pronounced “iffy”). Rewriting the pseudo-code above with an IIFE leads to the following code.Now a new environment is created for each iteration of the loop. The result is as expected:You can also make the color a parameter of the IIFE.Note that the example does have real-world relevance, because similar scenarios arise when adding handlers to DOM elements.