DEV Community

Stephanie Sison
Stephanie Sison

Posted on • Updated on

The Wonderful World of JavaScript Scopes

Understanding the three levels of Scope in JavaScript is very important. Once you have a better grasp on what can access each level of Scope, it makes life so much easier.

Let's think of this like those blocks that you played with as a kid. You know the ones that get smaller in size and can fit into each one to create just one box. Say you have three boxes. Box #1 is a little bigger than box #2, box #2 is a little bigger than box #3, and box #3 is the smallest of them all. Box #3 can fit into box #2, and then box #2 can fit into box #1 to create just one box. But box #1 cannot fit inside box #3. JavaScript works similarly. The different level of scopes can only be accessed through one direction and not the other.

Now that you have the images of those confusing boxes in your head, I can tell you about the Scopes of JavaScript.

You start off with the Global scope. This is similar to box number 1. With box #1 being the outer box that holds all the smaller boxes, Global scope is the outer scope. It can be accessed in every level of scope. If a variable is not declared inside a function or block scope, it is automatically part of the Global scope and it can now be referenced by anything in the code. This can be declared by using [const], [let], or [var]. Declaring a variable in a Global scope is not necessarily frowned upon, but it is much preferred for it to be in a functional or block scope. Only declare a variable via Global scope if you know for a fact that you need to be able to access it multiple times in your code.

const globalNum = 25;

function mathFunc() {
   return globalNum * 2 
} 

mathFunc()
// => 50
Enter fullscreen mode Exit fullscreen mode

As you can see from the above example, the globalNum can be accessed inside the mathFunc() with no problem and return the answer of '50' when mathFunc() is called.

Now we move on to box #2 or the Functional scope. A variable declared in the functional scope is specific to that function. Only that function has access to that variable. [const], [let], and [var] are keywords that can be used in a Functional scope.

const fruit = 'Apple'

function greens() {
  const vegetable = 'Spinach'

  console.log('fruit:', fruit)
  console.log('vegetable:', vegetable)
  console.log('sour:', sour)
}

function candy() {
  const sour = 'Airheads'

  console.log('fruit:', fruit)
  console.log('sour:', sour)
  console.log('vegetable:', vegetable)
}

Enter fullscreen mode Exit fullscreen mode
greens();
// LOG: fruit: Apple
// LOG: vegetable: Spinach
// ERROR: Uncaught ReferenceError: sour is not defined

candy();
// LOG: fruit: Apple
// LOG: sour: Airheads
// ERROR: Uncaught ReferenceError: vegetable is not defined
Enter fullscreen mode Exit fullscreen mode

We can see that both functions are allowed access to the fruit variable because it is in the Global Scope. But, because 'vegetable' is only declared in the greens(), the sour() is not able to have access to them. Which is why when we try to console log the variable sour in the greens() results in an ERROR. The same thing happens when we try to console log the variable greens in the sour().

And finally, box #3 or the Block scope. A Block scope is everything within a statement. It is very specific to that block and only has access to things inside its' block. Variables are typically declared in curly braces { }. Anything declared in the curly braces cannot be accessed outside of the block. The common keywords for a Block scope is [const] or [let]. The keyword [var] cannot be Block scoped because it does not follow the lexical pattern. It gives you access outside of block scope, but not global scope.

function myNum() {
   if(true) {
     let x = 100
     console.log('block:', x)
   } 
}
Enter fullscreen mode Exit fullscreen mode

If we call this function, it will allow us to console log x and return the value of it because we are using console.log inside the same brackets of the block scope.

myNum()
// block: x
Enter fullscreen mode Exit fullscreen mode

Now if we were to try to move the console log outside of the block scope, we would get an error because we are trying to access the variable 'x' from outside of the block scope. Like so:

function myNum() {
   if(true) {
     let x = 100
   } 
console.log('block:', x)
}
Enter fullscreen mode Exit fullscreen mode
myNum()
// ERROR : ReferenceError: x is not defined
Enter fullscreen mode Exit fullscreen mode

By simply moving the console log outside of the block scope parameters, the system will give us a ERROR Message.

Just like the box reference, you can only fit one inside another. The levels of scope can be accessed from down to up and not vice versa. The Global scope can be accessed by the Functional and Block scopes. The Functional scope can be accessed by the Block scope. And the Block scope only has access to itself. In a simplified version, the outer scope does not have access to variables declared in an inner scope. The inner scope, however, does have access to the outer scopes.

What matters the most in the scope chain, is where the variable is declared, not where it is invoked.

I hope my analogy made sense to you as it did to me. I find with as much information that we are currently learning, it is nice to relate it to something else to make sense of it all. Happy coding ya'll (:

Top comments (0)