Last Updated: February 25, 2016
·
1.019K
· Jason Balthis

Scope and Closures: Concepts That Every JavaScript Developer Should Understand

JavaScript is easily one of the most well known, used, and discussed languages that a developer has in his/her arsenal, which is why it is a shame that few developers are knowledgable about and take advantage of the concepts scope and closures.

To understand closures, scope must first be understood. Scope is simply the context in which a variable can be accessed by a method, function, etc. Scope can also be viewed as the level, lexically, that a variable is declared in. You may be asking yourself: "What the hell is that supposed to mean?" Simply put, this means that if you take a block of code that has curly-brackets, it's scope is anything outside (above) it's brackets and any variable declared directly within them. If a function or another block of code is constructed within its brackets, then the outer block wiill not have access to those variables declared within the inner block's brackets.

This brings our discussion to the two different types of scope that a variable can be classified as having:

  1. local
  2. global

Example of Local Scope

A variable said to have a local scope can only be accessed from within its local context, i.e within the same function/method in which it is decalred.


function myFunc(){

    // local_var is only accessible within myFunc() because it
    // was declared inside myFunc() using the 'var' keyword

    var local_var =  "I am a local variable and too good for you!";
    return
}

// undefined
var my_var = local_var;

Example of Global Scope

In contrast, a variable with global scope can be accessed from anywhere within the program, including from within other functions, methods,etc.


// global_var can be accessed by the other functions in the program
// because it was declared outside of them and within their scope

var global_var = "Anybody wanna hang?";

function myNewFunc(){
    console.log(global_var);
    return
}

More on Global Scope


// If declared without the 'var' keyword, but within a function, 
// the variable has global scope

function evenNewerFunc(){
    new_var = global_var;
    return
}

// test = true
var test = (new_var === "Anybody wanna hang?") ? true : false;

Scope Chain

From what has been demonstrated regarding scope, it follows quite logically that we can expect the nesting of functions within functions to result in the innermost function having access to all of the variables declared above or within itself, while the outer functions will only have access to the variables declared in the same level or a level above themselves in the scope. This resulting pattern is known as a 'scope chain,' A few lines of code should clarify. (Pay attention to what 'console.log()' has access to in each level of nesting.)


var outside = "It's kinda lonely out here";
console.log(outside);

function outer(){
    var in_outer = "I am in the outer function";
    console.log(outside+in_outer);
    function mid(){
        var in_mid = "The middle rocks!";
        console.log(outside+in_outer+in_mid);
        function inner(){
            var in_inner = "I am deep in here!";
            console.log(outside+in_outer+in_mid+in_inner);            
        }
    }
}

Issues Arise

As a result of the way that variables are declared in JavaScript and their resulting scope, a very important issue arises: How does one keep a function from accessing variables that are above it in the scope chain, which means are they are lexically reachable, but the function has absolutely no reason to be accessing them, whether this happens inadvertently or through exploitation? What if their values are able to be returned or modified? What if, currently, these values are bank account numbers? You get the picture.

Other programming languages, e.g. Java and C, have means to deal with this type of issue through the explicit declaration of public and private variables in order to provide better access control and then the use of pointers, local classes, read-only (final) declarations, etc. to allow the functions or methods that require access to certain variables to obtain and/or modify their values. Here is where JavaScript differs greatly from these other languages: It has no built-in means of dealing with access control other than the minute bit of security that lexical scope provides by default.

The Fix: Closures

Closures are JavaScript's answer to the problem of access control. A closure is simply a function or method that has the ability to return a reference to variables defined locally within itself and also return the referencing environment in which those variables were defined.

Let's look at some code to get an idea of how closures can be used and of the danger that can result from not using a closure in certain situations.

Modifying a Local Variable Within an Object


var myAcct {
    private_key: "123abc",

    verify: function(id){
        return(console.log(this.private_key === id));
    }
}

myAcct.verify("123abc");    // returns true
myAcct.verify("h9ck3d");    // returns false
myAcct.private_key = "h9ck3d";    // change value
myAcct.verify("123abc");    // returns false
myAcct.verify("h9cked");    // returns true

As can be seen above, in the right situation, all that has to be known is the parameter (variable) name within an object in order to manipulate and/or retrieve that parameter's value.

With that being said, closures can be used to provide the JavaScript equivalent of a private method and can easily be employed in a situation such as this to better provide security and and a level of access control that is not achievable by other means.

Private Methods in JavaScript via Closures

var myAcct = (function() {
    var private_key = "abc123";

    return{
        verify: function(id){
             return(console.log(private_key === id));   
        }
    } 
})();

myAcct.verify("123abc");    // returns true
myAcct.verify("h9ck3d");    // returns false
myAcct.private_key = "h9ck3d";    // no change to value
myAcct.verify("123abc");    // returns true
myAcct.verify("h9cked");    // returns false

The technique used in this example is known as a Immediately Invoked Function Expression (IIFE). It simply means that the function is invoked as soon as this block of code is executed and a reference to the private_key variable is stored along with the referencing environment in which it was stored. In this case this means that any subsequent calls to the myAcct closure function are simply retrieving the variable's value that was stored at the point of execution. No modifications can be done in this case, because there can only be modifications done to an object through whatever public interface that a method provides. If none is provided, nothing can be done.