Understanding Scope

What Scope Are We Talking About?

There are actually many types of scope. Two of the biggest you will deal with in programming are lexical scope and project scope. We are talking about lexical scope. Try to avoid dealing with project scope as long as you can because it is a nightmare and involves... human interaction...

Describing Scope

I'm not a fan of giving the text book meanings of things but I'll make an exception. Lets see what Dictionary.com says on the matter:

Extent or range of view, outlook, application, operation, effectiveness, etc.

What?!!!! That kind of makes sense but when you are learning scope it isn't very helpful. Lets try something like Wikipedia:

The strict definition of the (lexical) "scope" of a name (identifier) is unambiguous – it is "the portion of source code in which a binding of a name with an entity applies"

Ok, that is quite a bit better but c'mon. If you are just trying to learn what scoping is, that still doesn't really give it justice.

This is why I do the whole 'Programming For The Didactic By The Didactic' thing. Sometimes reading this stuff really just does not explain it.

Even if I were to try to sum it up in one sentence this would be my attempt:

The highest level in which a variable exists and exists within all of the nested code blocks.

Not even that was helpful. The problem is that there are multiple aspects that you need to be aware of and you can't explain it all in one sentence. What we need is some back story.

The Structure of A Program

Nested Code Blocks

So think of a program as a Matryoshka (One of those Russian nesting dolls). Each doll would be a code block. Each code block would have its own scope. Each code block (doll) also shares its parent's (larger doll's) scope. So the tiniest doll has its own scope but it can see everything from all the bigger doll's scopes. The bigger doll's, however, can't see the tiniest doll's scope.

Here is a rough example.


function doll1() {
    var some_doll1_variable = 1;
    // This is the biggest of the dolls.
    function doll2() {
        var some_doll2_variable = 2;
        // This is the second biggest doll.
        function doll3() {
            var some_doll3_variable = 3;
            //This is the tiniest of the dolls. (pretty boring Matryoshka)
        }
    }
}
  • Inside of doll3(), you have access to some_doll1_variable, some_doll2_variable and some_doll3_variable.
  • Inside of doll2(), you have access to some_doll1_variable, and some_doll2_variable but some_doll3_variable is out of your scope and as far as doll2 is concerned it doesn't even exist..
  • Inside of doll1(), you only have access to some_doll1_variable.
  • There is also a scope outside of doll1(). It is the global scope. Anything put in there is accessible inside of all three doll()'s

Complex Code Blocks

// Outside of crowd() is the global scope.  Lets call this the 'world' for now.
weather = 'snowing';

function crowd() {
    // Anything in crowd() knows that the weather is snowing because we are nested inside
    // the global scope.

    var total_people = 3;
    // The world (global scope) may now know there is a crowd in it but it
    // doesn't have the ability to see that there are 3 total_people.

    function barber() {
        // The Barber is one of the people.
        var children = 1;
        // Apparently the barber has a child.
        function barbers_child() {
            var candy_in_pocket = 3;
            // The child knows that the barber has one child.
            // The child knows that there are a 3 total_people in this crowd.
            // The child knows that it is snowing.
        }
        // Technically since barbers_child's code block just ended, it no longer
        // exists at all.  To make the story more compelling lets pretend
        // that the barber realises he has a child...
        //
        // Back in the Barber's world, that poor sucker has NO idea that his
        // child has 3 candies in his pocket.
    }
    // Again, now that the barber()s code block is finished he no longer technically
    // exists either but lets not ruin a good story.

    function confectioner() {
        var total_candies_in_shop = 55;
        // Unfortunately the confectioner knows that there are 3 total people.
        // He doesn't know who they are other than he is presumably one of them.
        // He knows he has 55 candies and that it is snowing.
    }
}

Well, it sucks for the confectioner and the child because neither of them know about eachother. The confectioner could be making bank of this kid and the kid could be riding the sugar high of a lifetime.

How can we fix this? We can make a way to keep track of everyone's candy at the crowd's scope. That way the status of the candy will be available to everyone.

weather = 'snowing';

function crowd() {
    // Now we have an associative array (hash/dictionary) for keeping track of everyone's candies.
    var candy_holder = {}
    var total_people = 3;

    function barber() {
        var children = 1;

        // Thusfar no one has any candy.  Barber can see the candy_holder
        // as it is in the crowd()'s scope thus in the barber's scope as well.
        // It, however, is currently entirely empty.

        function barbers_child() {
            // Now the kid, not knowing he should be hiding all of this candy
            // from his dad, hands his candies to the candy holder.
            candy_counter.barbers_childs_pocket = 3;
        }
        // At this point the child technically ceases to exist.  We'll just say he popped
        // into existence and popped out instead of saying he died some horrible death.
        // Lets keep it clean....

        // The barber takes a second look at the candy_holder and... hold on a minute!!!
        // Something called 'barbers_childs_pocket' has two lollipops and a gob stopper.
        // He's like, screw this!  I know I have one child so I'm going to assume this
        // is said child's candy.  (He's making a lot of assumptions here but I don't
        // want to rewrite this example for it to make a logical story.)
        // The barber decides that he is the one that works for the money
        // so he's going to take one of them lollipops from his non-existent son.
        candy_counter.barbers_childs_candy_in_pocket = 2;
        candy_counter.barber = 1;
        // Can you blame him?
    }

    // At this point, everyone in the crowd so far knows that...
    // Something called 'barber' has 1 candy.
    // Something called 'barbers_childs_pocket' has 2 candies.
    // Unfortunately techincally both the barber and the barbers_child no longer exist.
    // That's not important.  What is important is that their interactions with the
    // candy_counter still exist because has been in scope the whole time.

    function confectioner() {
        // The confectioner, being a sound businessman, adds his stock to the counter.
        candy_counter.total_candies_in_shop = 55;

        // Then he scans through the counter and he's like wait a minute...
        // Something called 'barber' has 1 candy and something called 'barbers_childs_pocket' has 2!!
        // He gets all excited and wants to go sell to someone.  Unfortunately, barber() and barbers_child()
        // are not in scope.  So all he knows is that he has 55 candies and supposedly the other 2
        // of 3 total_people have 3 other candies.
    }
}

More Abstract

Imagine the following hierarchy. All we are doing are creating variables and then displaying which variables exist in each section's scope (at the beginning and at the end of the scope..

  • Each scope is defined with { }.
  • Nested scopes inside scopes are indented for visual sake.
  • The letters preceeding ':' are being introduced for the first time.
  • The letters following ':' still exist within this scope.
  • Z is decalred outside of everything and thus, is global.
Z:

{A:Z

    {B:AZ
        {C:BAZ
            {D:CBAZ

            :DCBAZ}
        :CBAZ}
    :BAZ}

    {E:AZ
        {F:EAZ

        :FEAZ}
    :EAZ}

    {G:AZ
        {H:GAZ
            {I:HGAZ

            :IHGAZ}
        :HGAZ}
    :GAZ}
:AZ}

:Z

Summary:

Once a variable is declared within a scope it exists within that scope and all of that scope's children until the scope is gone. For all intents and purposes you can have any number of nested scopes in any arrangement.

Scope becomes very important when you start dealing with objects, but to understand why they are important, you must first understand how scopes work in the first place. Hopefully this article cleared that up.

All cards on the table, I recently injured my back so I'm writing this wish some serious medication going through my veins. It is entirely possible that none of this makes sense...

I will be writing up a follow-up advanced article about scoping in JavaScript in regards to objects and the dangers of the global scope.