Fork me on GitHub

Ahad Bokhari

Lexical Scope

Edit   ·   words

Grass roots concepts in JavaScript is scope consisting of global, local and nested scopes as well as closures & hoisting (I’m sure you have heard these terms before). These are core concepts that JavaScript is made of and a fundamental understanding of them is very important. There is no substitute for learning JavaScript at it’s core first, even before you start playing with the shiny new frameworks out there.

You might be judged on how well you understand just scope, so let’s view some concise examples in terms of variables and not paint with a broad brush leaving excuses for not understanding the basics.

Global Scope

A simple definition is the global scope is the scope visible in all other scopes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// a is a global variable
var a = 20;

function b() {
console.log("I am a global function logging " +
a + " to the console"); // access to global var
}

b(); // execution of a global function which logs 20

function c() {
console.log(a);
}

c(); // 20

In the above example the variable a is accessible from any function; we’re working with variables for the most part but understand that functions also have a global scope and can be access from anywhere in your application. So, there is a global scope in which reside global variables and global functions.

Local Scope

Local variables can only be accessed within the local scope that they are created in - therefore when scripting with JavaScript the only way can be defined by a function. If you define a variable in a function, you can only get access to it within that function. Else, you will get a ReferenceError.

1
2
3
4
5
6
7
8
9
10
var a = 100;

function b() {
var c = 200; // local variable
console.log(c);
}

b(); // 200
console.log(c); // Reference Error: c is not defined
console.log(a); // 100, since in global scope

Let’s take another example but this time we’ll define an implicit global without using var.

1
2
3
4
5
6
7
8
9
10
11
var a = 101;
function b() {
var x = 10, // local
y = 20; // local
z = 30; // implicit global (,)
console.log(x, y, z); // 10, 20, 30
}

console.log(x); // ReferenceError
console.log(y) // ReferenceError
console.log(z); // 30

We can clearly see in the rare case we use an implicit global we can access it outside it’s scope like any other global variable. It is not recommended to pollute the global namespace. What if we have two variables a, one global and one local? How does that get interpreted?

1
2
3
4
5
6
7
8
9
10
11
12
var a = 55;
function b() {
console.log(a);
}

function c() {
var a = 50;
console.log(a);
}

b(); // 55
c(); // 50

As a bonus it’s common to create your code and encapsulate all your variables to a local scope using an IIFE or immediately invoking functional expression - this way you can properly isolate your variables. IIFE’s are a JavaScript design pattern used by popular libraries like Backbone.js, Modernizr and jQuery.

The syntax is simple:

1
2
3
(function() {
// my code goes here
}());

There are more benefits to an IIFE and you should definitely read more about them.

Block Scope

There is no block scope in JavaScript, but ES6 introduces let:

1
2
3
4
5
6
7
8
var a = 5;

function b() {
if (true) {
var a = 10;
}
console.log(a); // alerts 10, not global var 5
}

Multiple Scope

Of course you will have many functions and variables in your program. This creates a scope for each of them as you execute and that execution scope is ultimately linked to global scope. Confused? Let’s learn by example:

1
2
3
4
5
6
7
8
9
10
11
12
var a = 10;

function one() {
console.log(a);
}

function two() {
console.log(a);
}

one(); // new scope created for execution of fn one
two(); // new scope created for execution of fn two

Here is a simple example of nested scope and can also be called inner functions which lead to closures.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 5;

function b() {
var d = 30;
function c() {
console.log(a);
}
return c(); // creates a closure
}

function d() {
console.log(a);
}

b(); // 5
d(); // 5

What’s interesting to note with the above example is that the b() inner function was returned from the outer function before
being executed therefore creating a closure. Closures are not a new phenomenon and are a powerful feature of JavaScript - they do come with performance constraints, therefore it’s appropriate to follow some of these rules:

  • Try not to create too deep of a nested scope
  • Don’t use a closure if you don’t need a closure
  • Memory leaks are caused when you hold excessively large private memory

A closure is created when you execute a function within a function. Closures save the state of the outer function variables and do not expose those private variables. Look up encapsulation.

Note: Though not at all comprehensive when speaking of closures, but you should take away that this is what you need to know to understand modern day JavaScript.

How JavaScript Sees Variables

JavaScript is a lexically scoped language. To understand scope, scope execution and the scope chain (which is crucial to understanding closures) better we have to understand how JavaScript sees things. When traversing the scope chain JavaScript follows these steps, every single time:

  • Local Scope: JavaScript checks local variables first, if so then JavaScript will go ahead and give it to you. Local scope takes importance.
  • Parameters: After checking for the variable locally, JavaScript looks at the parameters.
  • Outer Scopes: If nothing is found in local scope, or parameters JavaScript crawls outside of the function via the scope chain of your current scope.
  • Window: Any variable that exists in the global scope will be a property of the global ‘window’ object
  • Undefined: If your variable doesn’t exist anywhere then JavaScript will respond with “undefined”

A really fantastic reference that can be found on Github: You Don’t Know JS: Scope & Closures/. Always give performance and memory importance as a habit when writing code. This can be a whole new topic and I suggest you look it up.

Hoisting

As a last concept to grasp, let’s take a brief look at hoisting. You can read more about it but let’s touch base on what it’s all about to get you started. The general definition of hoisting is a device that is used for lifting or lowering a load.

JavaScript does exactly the same thing, as it hoists (lifts or raises) a variable declaration var a = 15 and a function declaration function qux() {..} to the top of the function scope. Let’s take an example and learn from it:

1
2
3
4
5
6
function norf() {
console.log(b);
var b = 69;
}

norf() // undefined

In the code block above norf() returns undefined where you expect it to have a ReferenceError. The reason here is when the JS engine is creating a context for the function it scans through and brings the entire variable declaration to the top. In this case when the variable declaration is hoisted (not the value) and it automatically gets the default value of undefined.

In the following days I’ll put up some more interesting examples of hoisting and I hope you retain some of this information and take away some of the most important concepts.