DEV Community

Kelvin
Kelvin

Posted on

Deep dive JavaScript: Scope

Are you always wondering how JS can understand our code, how scopes can be created?

You'll need to have a better mental model about how JavaScript engine handles our code in general. In this article, we are going through how JS scopes are created under the hood, step by step like:

  • Compilation
  • Parsing/Compilation phase
  • Evidences of code compilation
  • Briefly about lexical scope

What is Compiled and Interpreted?

Compilation is a process that turns your code into a list of instructions that your machine can understand

Interpretation is similar to compilation but instead of processing the whole source code, it will be processed line by line. Each line is executed before proceeding to process the next line.

Note: new JS engines actually use both compilation and interpretation to handle JS programs.

Step by step through compilation phase

JavaScript code is processed in 2 phases: Parsing/Compilation and Execution

Parsing/Compilation phase will be our main concern in this article.

Parsing/Compilation happens in 3 basic stages:

  • Tokenizing/Lexing: consider var a = 7; the program will likely to break this up into tokens: var a = 7 ;
  • Parsing: turn tokens into an Abstract Syntax Tree (AST)
<VariableDeclaration>            var
    <Identifier>                  a
        <AssignmentExpression>    =
            <NumericLiteral>      7
Enter fullscreen mode Exit fullscreen mode
  • Code generation: take the AST and turn it into a set of instructions to actually create a variable called a and assign a value to it

Note: JS compilation only happens milliseconds right before the code is executed.

How do we know that compilation happens in 2 phases?

There are 3 cases that you can see that JS will handle your programs in at least 2 phases: parsing/compilation ⇒ execution

  • Syntax errors
  • Early errors
  • Hoisting

Case 1:

Consider:

const foo = 'cat'

console.log(foo)

const error = #7'dog' // Throw a syntax error here
Enter fullscreen mode Exit fullscreen mode

If you run this code, you can observe that the program will throw the error first instead of logging 'cat' to the console.

This example shows that JS engine know about the syntax error on the third line before executing first and second line, by parsing the entire program before executing it.

Case 2:

Consider:

'use strict'

console.log('cat')

function saySomething(pet, pet) {
    console.log(pet)
}

saySomething('dog', 'fish') // Uncaught Syntax error: Duplicate param name not allowed
Enter fullscreen mode Exit fullscreen mode

Again here, how can JS engine throw the error without logging 'cat' to the console first? The answer is the code must be fully parsed before any executions happen.

Note: duplicate param name is not allowed in strict mode, but it's allowed in non-strict mode.

Case 3:

Consider:

function myPet() {
    var dogName = 'Doggy';
    {
        dogName = 'Bata'; // error
        let dogName = 'Lucky'; 
        console.log(dogName)  
    }
}

myPet() 
// ReferenceError: Cannot access 'greeting' before
// initialization
Enter fullscreen mode Exit fullscreen mode

Technically, the error is thrown because dogName = 'Bata' is accessing variable dogName before it's declared on the next line.

But why it does not access the variable var dogName rather than accessing the let dogName ?

The only way JS engine would know is that:

  • JS processes the code first ⇒ comes to the statement let dogName = 'Lucky' ⇒ declare the a blocked-scope ⇒ set up all the scopes and their variable associations.

As you can see now:

  • JS code is parsed before any execution
  • Scope is determined as the program is compiled, and will not change during run-time.

Lexical scope

If you declare a variable with var inside a function ⇒ the compiler handles this declaration as it's parsing the function ⇒ associates the variable with function's scope (the variable is accessible anywhere inside the function) .

If you declare a variable with let or const ⇒ compiler handles this declaration ⇒ associates the variable with block's scope (variable is accessible inside the nearest {...} rather than its enclosing function).

While scopes are identified during compilation, they're not actually created until runtime.

The scope of one variable, function or block cannot be changed later.

Summary

Compilation is a set of steps that process the text of your code and turn it into a list of instructions that computer can understand.

JS code is processed in 2 phases: Parsing/Compilation and Execution

The Parsing/Compilation phase only happens in microseconds right before the code is executed

Scopes are identified during compilation

Lexical scope is controlled entirely by the place where functions, blocks, variables are declared

Thank you for your time.

Top comments (0)