JavaScript Scope (or not)


Part 4


JavaScript Scope (or not)

The way JavaScript does scope is different to most C-like languages. It has curly braces like C, but doesn't in fact work in the same way. This can be quite confusing.

When you have a code block inside another block, such as a loop inside a function, or an if statement block inside a loop, the inner block leaks variables to the outer block.

In the following example, the word and i variables defined within the if block are visible outside the if block:

var str = "Hello Everyone!";

if (str) {
 
    var i = 0;
    var word = "";
    
    while (i < str.length && str[i] !== " ") {
        word += str[i];
        i++;
    }
}

alert(word); // Hello
alert(i);    // 5

This wouldn’t happen in most C-like languages. Secondly, consider this:

var a = 2;

for (var a = 0; a < 10; a++) {

}

In C#, this wouldn't compile. It would say:

'A local variable named 'a' cannot be declared in this scope because it would give a different meaning to 'a', which is already used in a 'parent or current' scope to denote something else'.

In JavaScript though, you are allowed to do that. But it may not do what you expect:

var a = 2;
    
for (var a = 0; a < 10; a++) {
    
}
    
alert(a); // is now 10!

The two a variables are one and the same, and after the loop a is 10 not 2.

Functions and Scope

There is one type of block which doesn't leak scope: the function. These behave in a rather more useful way:

Here's an example that uses these features of functions:

// Split a string into words.  Obviously there are better ways to do this - it's just an example!

var getWords = function(str) {
    
    var i = 0;
    var words = [];
    
    var getNextWord = function() {
    
        // The inner function can access the variables in the outer function (i and str).
        // But the local 'word' variable is encapsulated inside this function.
    
        var word = "";
        
        while (i < str.length && str[i] !== " ") {
            word += str[i];
            i++;
        }
        
        return word;
    };
    
    // This variable does not clash with the 'word' variable in getNextWord(), as the latter is only 
    // visible inside the getNextWord function.
    
    var word = getNextWord();    
        
    while (word) {     

        words.push(word);
        
        i++; // skip space character
        
        word = getNextWord();
    };
    
    return words;
};

var words = getWords("Hello Everyone!"); // ["Hello", "Everyone!"]

Summary

While JS blocks don't scope variables as they do in most C-like languages, you can use functions to achieve encapsulation.

Functions inside functions are common in JS, and many patterns use this approach to provide encapsulation and reuse.

Functions encapsulate their inner variables, but can also access variables from their outer scope. This makes JS functions extremely powerful, as we'll see in the next section.

JavaScript code blocks do not scope variables. Apart from functions, which do encapsulate their innards.