Scopes & Closures

Parminder Singh

-YDKJS series

Chapter 1: What's the Scope?

  • To understand closely the underlying mechanisms used by the engine to organize and manage variables
  • How does JS know which variables are accessible by any given statement
  • How does it handle two variables of the same name?
  • Answer lies in the form of well-defined rules called "scope"

     

Intro

  • JS is typically classified as an interpreted scripting language
  • Its assumed that JS programs are processed in a single, top-down pass.
  • But JS is in fact parsed/compiled in a separate phase before execution begins. Two phase execution.

Intro

let x = 20,
    y = 10;

let result = add(x,y);
console.log(result);

function add(a, b){
  return a + b;
}
  • The code author's decisions on where to place variables, functions, and blocks with respect to each other are analyzed according to the rules of scope, during the initial parsing/compilation phase.
  • The resulting scope structure is generally unaffected by runtime conditions.
  • Functions hold and access variables, they maintain their original scope no matter where in the program the functions are eventually executed. This is called closure.

Intro

  • Compilation is a set of steps that process the text of your code and turn it into a list of instructions the computer can understand.
  • The whole source code is transformed at once, and those resulting instructions are saved as output (usually in a file) that can later be executed.

Compiled vs. Interpreted

  • With interpretation the source code is transformed line by line
  • Each line or statement is executed before immediately proceeding to processing the next line of the source code.

Compiled

Interpreted

Compiled vs. Interpreted

Compiled vs. Interpreted

Note: Modern JS engines actually employ numerous variations of both compilation and interpretation in the handling of JS programs.

Conclusion: JS is most accurately portrayed as a "Compiled" language

Code Compilation

Scope is primarily determined during compilation 

Why does it even matter whether JS is compiled or not?

Code Compilation

Three stages of Code Compilation

  • Tokenizing/Lexing - Breaking up a string of characters into meaningful chunks, called "tokens"

       var a = 2;  ➝  var, a, =, 2, ;

      Lexing is a subtle variation of Tokenizing in the sense  that it identifies tokens in a stateful way.

 

 

Code Compilation

Three stages of Code Compilation

  • Parsing - Taking a stream (array) of tokens and turning it into a tree of nested elements viz AST(Abstract Syntax Tree)

 

Code Compilation

Three stages of Code Compilation

  • Code Generation - taking an AST and turning it into executable code

 

Machine Instructions - to actually create a variable called a (including reserving memory, etc.), and then store a value into a.

JS Engine

  • Vastly more complex than just these three stages
  • Steps for performance optimizations involved
  • Code can even be re-compiled and re-optimized during the progression of execution.
  • No luxury of an abundance of time - no build step ahead of time unlike other languages.
  • JS engines use all kinds of tricks (like JITs, which lazy compile and even hot re-compile)

JS Engine - Two Phases

Conclusion/Fact: Processing of JS programs occurs in (at least) two phases:

  1. Parsing/compilation
  2. Execution

 

 

Compile-then-execute approach

JS Engine - Two Phases - Proofs

Syntax Errors

var greeting = "Hello";

console.log(greeting);

greeting = ."Hi";
// SyntaxError: unexpected token .

JS engine first parses the entire program before any of it is executed 

Syntax Errors

Syntax Errors

JS Engine - Two Phases - Proofs

Early Errors

console.log("Howdy");

saySomething("Hello","Hi");
// Uncaught SyntaxError: Duplicate parameter name not
// allowed in this context

function saySomething(greeting,greeting) {
    "use strict";
    console.log(greeting);
}

strict-mode forbids duplicate params

Error thrown is not syntax error but early error in strict mode

JS Engine - Two Phases - Proofs

Hoisting

function saySomething() {
    var greeting = "Hello";
    {
        greeting = "Howdy";  // error comes from here
        let greeting = "Hi";
        console.log(greeting);
    }
}

saySomething();
// ReferenceError: Cannot access 'greeting' before
// initialization

Accessing the greeting variable too early, a conflict referred to as the Temporal Dead Zone (TDZ)

  

JS Engine - Variables Identification

var students = [
    { id: 14, name: "Kyle" },
    { id: 73, name: "Suzy" },
    { id: 112, name: "Frank" },
    { id: 6, name: "Sarah" }
];

function getStudentName(studentID) {
    for (let student of students) {
        if (student.id == studentID) {
            return student.name;
        }
    }
}

var nextStudent = getStudentName(73);

console.log(nextStudent);
// Suzy

Target vs Source value

var a= 2;

Targets - 5

Sources - 7

Cheating: Runtime Scope Modifications

function badIdea() {
    eval("var oops = 'Ugh!';");
    console.log(oops);
}
badIdea();   // Ugh!

In non-strict mode only.

eval compiles the string it has and executes it at runtime

Any vars or function declarations would modify its current scope of execution.

Performance Hit - Bad for already optimized code

var badIdea = { oops: "Ugh!" };

with (badIdea) {
    console.log(oops);   // Ugh!
}

Lexical Scope

  • Refers to lexing stage of compilation
  • Controlled entirely by the placement of functions, blocks, and variable declarations, in relation to one another.
  • If you place a variable declaration inside a function, the compiler handles this declaration as it's parsing the function, and associates that declaration with the function's scope.
  • If a variable is block-scope declared (let / const), then it's associated with the nearest enclosing { .. } block, rather than its enclosing function (as with var).
  • Compilation creates a map of all the lexical scopes

Scopes & Closures - Chapter 1

By Param Singh

Scopes & Closures - Chapter 1

  • 372