Scope in JavaScript - Behind The Scenes
Tags: javascript, beginners, webdev, scope
Published: 2021-09-11 11:57:20

Scope basically means *__" the chance or opportunity to do something"__*. But what does it mean w.r.t JavaScript, and how do JavaScript engines interact with it to process any line of code. Let's find out-

##What is Scope?
Scope collects and maintains a look-up list of all the declared identifiers (variables), and enforces a strict set of rules as to how these are accessible to currently executing code. 
To understand this, let's briefly see how compilation works for a piece of code that the Engine encounters.

##Steps involved in compilation

####1. Tokenizing/Lexing
This involves breaking up a string of characters into small chunks, called tokens. For example, `const foo = 4;` may be broken into `const`, `foo`, `=`, `4`, and `;`.
A tokenizer breaks a stream of text into tokens, usually by looking for whitespace (tabs, spaces, newlines). A lexer is basically a tokenizer, but it usually attaches extra context to the tokens -- *this token is a number, that token is a string literal, this other token is an equality operator*.

####2. Parsing
Turning a stream(array) of tokens and turning it into a tree of nested elements, which collectively represents the grammatical structure of the program. This tree is called Abstract syntax tree.
To see how an AST looks like follow this [link](https://astexplorer.net/#/gist/28c022eb0faf393edb031c4b344c9e3f/f24bc4a52291ca799fd645d709852d4cbd729816).

####3. Code generation
This involves taking an AST and turning them into executable code. 

The JS engine is vastly more complex than just these three steps. For instance, there are steps to optimize the performance of the execution which we'll cover in another post. But when do scope comes to picture during these steps 🤔.

## Here's when scope comes to the picture
Consider this expression `const foo = 4;`. Once the compiler is done with tokenizing and parsing this expression, it'll go for code generation, and proceeds as follows:
1. On encountering `const foo` compiler will ask Scope if a variable named `foo` already exists for that particular Scope collection. 
If so, the Compiler ignores this declaration and moves on. Else, It asks Scope to declare a variable named `foo` for that scope collection.
2. Compiler then produces code for the Engine to execute. 
To handle `foo = 4` assignment, Engine asks scope if there is a variable called `foo` accessible in the current scope collection. If so, the Engine uses that variable. 
Otherwise, it looks in the scope outside of the current scope until it finds variable `foo` or reaches global scope.

If the Engine eventually finds a variable named `foo`, it assigns the value to it. Otherwise, it will raise a `ReferenceError`.

Consider the following program:
```js
const a = 4;

function baz() {
  const b = 2;
  
  function bar() {
    console.log(b);
  }
  
  function fam() {
    console.log(a);
    console.log(c);
  }
  
  bar();
  fam();
}

baz();
```

Consider the functions `bar()` and `fam()` in above program. 

On encountering `console.log(b);` in `bar()` Engine will ask Scope if there is a variable named `a` accessible in `bar()`'s scope collection. Scope will say *"Nope, never heard of it. Go fish"*. Then the Engine will ask the same question to `baz()`'s Scope, and here is what it says *"Yep, it's there. Here ya go"*.

The same steps are followed while running `console.log(a);` in `fam()`. The only difference is, Engine won't find the variable until it reaches the Global scope, where it'll find `a`.
Next, I'll try and run `console.log(c);` but won't be able to find it in the Global scope collection either. At last, the Engine will raise its hand and yell out a `ReferenceError`  

Here is a representation of the flow:

![Scope flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnxlxpkthen3o3xltkfk.jpg)

Source
[1]: You Don't Know JS: Scope & Closures By Kyle Simpson