The Wonderful World of JavaScript Scopes
Tags: javascript, beginners
Published: 2022-06-06 22:29:00

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
```
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)
}

```

```
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
```

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)
   } 
}
```
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
```
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)
}
```
```
myNum()
// ERROR : ReferenceError: x is not defined
```
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 (: