In our last two posts, we started looking at functions and objects in JavaScript. Both functions and objects make use of variables and because of this, we must discuss the concept of scope in JavaScript before going further. In fact, objects and functions themselves are also subject to scope in JavaScript because they are also treated as variables.  Scope is a concept that you will see in many different programming languages and it is much more powerful than it seems at first.

So, what exactly is scope and how does scope in JavaScript work specifically? Scope refers to the set of variables, objects, and functions that you have access to at a given area of a program. In JavaScript, where you define a variable is extremely important because it determines the “visibility” of that variable in different parts of the code. In many other languages, you have access to different keywords that let you change the scope of a variable but this is not the case in JavaScript.

Scope in ECMAScript

You have two main forms of scope in JavaScript (pre-ES6); global scope and local scope. The upcoming iteration of JavaScript, ECMAScript 6 (ES6), also features the ability to bind a variable to a block; this is called block scope. To better demonstrate what scope is, take a look at the code below in our JavaScript console.

As you can see, we have defined the variable x on three separate occasions in this code. Our first declaration is what is called a global declaration. This means that all of the code has access to this variable. The variables x that are defined inside of each of the functions are local variables. These variables can only be accessed by the functions themselves. Notice that the function, example_1 also uses a local scope. The x referred to inside of example_1 is the argument passed into example_1.

scope_chainingIn a way, scope refers to the lifetime of variables; that is, how long variables stay in memory. In the case of globally scoped variables, the variables will stay in the ram until you close the program (or browser window).  With locally scoped variables, the variable only lasts until the function it’s scoped to is executing. Let us take a look at this next example to see some of the quirkiness of the scope in JavaScript.

It is worth noting that using a return statement can change the scope of a variable in an artificial way. If you return a variable from a function then it is still visible in the parent code body.  In this way, you are extending the lifetime of a locally scoped variable by passing it into it’s parent.

Strange Scoping

What do you think will happen when we run the strange function? common sense tells us that the function will print 1 then 30 to the console, but this is not actually what happens. We see undefined, then only one print declaration of 30 and then another undefined. The reason for this is that JavaScript always moves the variable declaration to the top of the scope. The function strange should actually look something like this.

strange_2 also gives us the same output as strange did. If you define a variable but do not give it a value and then pass it to the console.log() function, you will see undefined print to the console then get a return value of undefined as well. This is essentially what is happening with strange and strange_2. The variable declaration of x inside of the function becomes locally scoped to the function and it is passed undefined into the first console.log(x) statement. x then receives the value of 30 and it is printed to the console.

The Let Keyword

es6-letBefore we wrap this up, let us take a small peak at block scope from ES6. ES6 introduces many different keywords to JavaScript, one of which is the let keyword. The let keyword works like a var declaration but it is specifically made for block scoping.  Here is an example of block scoping using a modified version of the example_3 function from before.

The non_block function suffers from a similar problem to our strange function in that the var x inside of it is pushed to the top of the scope. This causes the first console.log(x) statement to print undefined and then print "block" twice. In our block_scope function, the let keyword does not become a locally scoped variable because it’s bound to the if statement instead of the function. This is why we get "global" "block" "global" as our print outs.

Like global and local variables, a block scoped variable only “lives” in the code until the code-block it is scoped to finishes running. In this case, the if statement finishes running and the let x binding dies before the last console.log(x) statement is run.

Conclusion

In this post, we looked at scope in JavaScript. We saw examples of local scope, global scope and block scope. We introduced ES6’s let keyword and looked at some of the strange examples that came from pre-ES6 scoping rules. In our next post, we will continue our journey and expand upon what we already know of objects and functions.