Arieh.co.il

Javascript Guide - Scope Resolution

You can now also navigate the guide through the table of contents.

In the previous post we learned about functions, and it is time we learn some of their key features.

We are now side-stepping to a more complex, yet vastly important aspect of javascript - the scope resolution mechanism - or - why do we keep on using var.

The global scope

First of all - what is a scope - a scope is the current stack of variables and functions that our program uses. In JS, this feature is quite complex, but also very powerful.

Up until now, whenever we declared variables, we did that in the global scope. In javascript, there is a hidden, object-like variable, that contains a list of all the variables and functions declared on our program. In the browser, that object is called window.

    var a = 'abc';
    console.log(window['a']);
    
run code

Whenever we try to use a variable on the global scope, the window object is scanned for that variable's name, and returns its value.

But here is where it gets tricky - when we create a function, we create a new scope. This scope has access to all the variables of it's parent's scope:

    var a = 'abc';
    
    function useScope(){
        console.log(a);
    }
    
    useScope();
    
run code

As you see, although a was declared on a different scope, it is accessible everywhere within our program. It gets even better - we can also manipulate variables of outer scopes:

        var a = 'abc';
        
        function manipulateVar(){
            a = '12a';
        }
        
        console.log(a); // abc
        manipulateVar(); 
        console.log(a); //12a
    
run code

But as powerful as this might seem, it is also very risky.

Another important note is that whenever we create a variable without the var keyword, it will be created on the global scope:

        
        function manipulateVar(){
            a = '12a';
        }
        
        console.log(a); // undefined
        manipulateVar(); 
        console.log(a); //12a
    
run code

These 2 features make it extremely easy to make mistakes. It is because of this that I told you that we should always use the var keyword. You see, whenever a variable is declared using the var keyword, it is accessible only to that scope and it's children.

So, if we want to make sure we are not manipulating global variables, we should do:

        var a = 'abc';
        
        function manipulateVar(){
            var a = '12a';
        }
        
        console.log(a); //abc
        manipulateVar();
        console.log(a); //abc
    
run code

In the above example, 2 as were defined, one for each scope. As a best practice, we should always use the var keyword, unless we explicitly want to access a variable from a parent scope.

Scope Nesting

If that wasn't confusing enough, there is more - since each function is a new scope, it has all the properties of a scope - and so we can declare whatever we want within it. That means that we can create new functions within that scopes, and these will have private scopes of their own.

        var a = 'global -aa-',b='global -bb-',c='global -cc-';
        function myScope(){
            var c = 'myscope -ccc-';
            function anotherScope(){
                var b = 'anotherScope -bbb-';
                console.log(a+b+c);
            };
            
            anotherScope();
            
            console.log(a+b+c);
        };
        
        myScope();
        
            
        console.log(a+b+c); 
        //global aa anotherScope bbb myscope ccc
        //global aa global bb myscope ccc
        //global aa globa bb  gobal cc
    
run code

This was by far the most complex example yet, for at least 3 reasons:

  1. Function Nesting - we have a function that is nested within another, and called by it.
  2. Execution order - the global scope calls myScope that calls anotherScope.
  3. Scope nesting - each scope uses some of it's parent scopes' variable, and declares it's own. It is this confusion that we try to avoid unless necessary (and sometimes it can be extremely useful).

I suggest you try experimenting with this for a while. Although quite confusing, it is also mandatory that we understand this as we go along, at least at the base level.

Anonymous Closures

OK. This part will be a bit more advanced than what we've done so far. We will use a lambda to create a one-time scope. Before we begin however, I want us to understand how parenthesis work in JS

Whenever we put a statement inside ( ), that statement will be executed, and it's value will be returned. This has many uses, for example - numbers have a lot of methods attached to them, but the ., when used with a number, will simply be treated as a decimal separator. So, instead, we can do this:

    (1.2).toString();
    
run code

What we do with anonymous closures, is that we use the ( ) to invoke a lambda, and then execute it:

    (function(){
        //private_a will only be accessible within this scope
        var private_a = 'a';
        
        //since this one doesn't use var, it will be defined on the global scope
        A = function(){console.log(private_a);}
    })();
    
    A(); //a
    
run code

So, what we did here, is to create a private variable, that is only accessible to the code within the scope. The reason we would want to avoid what is called global scope pollution.

What this means is simple - when we write our webpages, we often find ourselves using other people code. If that code uses a lot of global variables, there is a good chance we would find ourselves stepping on other people's toes. By using the above pattern, we can create as many variables as we want, and still expose only the bare minimum to the global scope. It will also help us along the way to create a better encapsulated code.

Next Post - the Prototype

JavaScript Reference, JavaScript Guide, JavaScript API, JS API, JS Guide, JS Reference, Learn JS, JS Documentation