Chapter 1: What’s the Scope?
By the time you’ve written your first few programs, you’re likely getting somewhat comfortable with creating variables and storing values in them. Working with variables is one of the most foundational things we do in programming! But you may not have considered very closely the underlying mechanisms used by the engine to organize and manage these variables.How does JS know which variables are accessible by any given statement, and how does it handle two variables of the same name?
About This Book
Welcome to book 2 in the You Don’t Know JS Yet series! If you already finished Get Started (the first book), you’re in the right spot! If not, before you proceed I encourage you to start there for the best foundation.Focus on the Scope System
Our focus will be the first of three pillars in the JS language: the scope system and its function closures, as well as the power of the module design pattern.
Understand Two-Phase Processing
JS is parsed/compiled in a separate phase before execution begins. The code author’s decisions on where to place variables, functions, and blocks are analyzed according to the rules of scope during compilation.
Compiled vs. Interpreted
You may have heard of code compilation before, but perhaps it seems like a mysterious black box where source code slides in one end and executable programs pop out the other.What is Compilation?
Code 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. Typically, the whole source code is transformed at once, and those resulting instructions are saved as output that can later be executed.What is Interpretation?
Interpretation performs a similar task to compilation, in that it transforms your program into machine-understandable instructions. But the processing model is different:Unlike a program being compiled all at once, with interpretation the source code is transformed line by line; each line or statement is executed before immediately proceeding to processing the next line.
Is JavaScript Compiled?
Recall that we surveyed this topic in Chapter 1 of the Get Started book. Our conclusion there is that JS is most accurately portrayed as a compiled language.Compiling Code
In classic compiler theory, a program is processed by a compiler in three basic stages:Tokenizing/Lexing
Breaking up a string of characters into meaningful chunks called tokens.For example,
var a = 2; would be broken into: var, a, =, 2, and ;.Parsing
Taking a stream of tokens and turning it into a tree of nested elements, which collectively represent the grammatical structure of the program. This is called an Abstract Syntax Tree (AST).For
var a = 2;, the AST might have:- A top-level node called
VariableDeclaration - A child node called
Identifier(whose value isa) - Another child called
AssignmentExpressionwith a childNumericLiteral(whose value is2)
The JS engine is vastly more complex than just these three stages. In the process of parsing and code generation, there are steps to optimize performance, including collapsing redundant elements. Code can even be re-compiled and re-optimized during execution!
Required: Two Phases
To state it as simply as possible: The separation of a parsing/compilation phase from the subsequent execution phase is observable fact, not theory or opinion. There are three program characteristics you can observe to prove this:1. Syntax Errors from the Start
"Hello" is not printed), but instead throws a SyntaxError about the unexpected . token.
2. Early Errors
"Howdy" message is not printed. The SyntaxError is thrown before the program is executed, because strict-mode forbids functions to have duplicate parameter names.
How does the JS engine know that
greeting parameter has been duplicated, and that saySomething(..) is in strict-mode (the "use strict" pragma appears later in the function body)?Again, the only reasonable explanation is that the code must first be fully parsed before any execution occurs.3. Hoisting
ReferenceError occurs from the line with greeting = "Howdy". The greeting variable for that statement belongs to the declaration on the next line, let greeting = "Hi", rather than the var greeting = "Hello" statement.
Compiler Speak
With awareness of the two-phase processing of a JS program (compile, then execute), let’s turn our attention to how the JS engine identifies variables and determines the scopes of a program as it is compiled. Let’s examine a simple JS program to use for analysis:Targets and Sources
Other than declarations, all occurrences of variables/identifiers in a program serve in one of two “roles”:Target
The variable is the target of an assignmentExamples:
students = [...]studentinfor (let student of students)- Function parameter
studentID
Source
The variable is the source of a valueExamples:
studentsin the for-loopstudent.idandstudentIDin the if statementgetStudentNamein the function call
Targets in Our Program
students = [...]- clearly an assignmentfor (let student of students)-studentis assigned a value each iterationgetStudentName(73)- the argument73is assigned to parameterstudentIDfunction getStudentName(studentID)- the function declaration is a special target reference
Sources in Our Program
studentsin the for-loop statementstudentandstudentIDin the if conditionstudent.namein the return statementgetStudentNameandnextStudentin their respective referencesconsoleinconsole.log(nextStudent)
Properties like
id, name, and log are not variable references - they’re properties, not variables.Cheating: Runtime Scope Modifications
eval(..)
Theeval(..) function receives a string of code to compile and execute on the fly during runtime:
eval(..) had not been present, the oops variable would not exist and would throw a ReferenceError. But eval(..) modifies the scope of the badIdea() function at runtime.
with Keyword
Thewith keyword dynamically turns an object into a local scope:
Lexical Scope
We’ve demonstrated that JS’s scope is determined at compile time; the term for this kind of scope is “lexical scope”. “Lexical” is associated with the “lexing” stage of compilation.The key idea of “lexical scope” is that it’s controlled entirely by the placement of functions, blocks, and variable declarations, in relation to one another.
How Lexical Scope Works
- If you place a variable declaration inside a function, the compiler associates that declaration with the function’s scope
- If a variable is block-scope declared (
let/const), it’s associated with the nearest enclosing{ .. }block - If a variable is declared with
var, it’s associated with the nearest enclosing function (or global scope)
Variable Lookup
A reference (target or source) for a variable must be resolved as coming from one of the scopes that are lexically available to it:Summary
In this chapter, we established that:- JavaScript is a compiled language with two-phase processing
- The compilation phase determines lexical scope
- Variables serve as either targets (assigned to) or sources (read from)
- Lexical scope is based on the placement of variables and blocks in code
- Scope modifications at runtime (via
evalorwith) should be avoided

