You Don't Know JS: Book 2 (Scope and Closure) - 01 What's the Scope?
My notes from Kyle Simpson's (aka getify's) 2nd book, spoiler alert: twas difficult 😅
Answering the following two questions:
- how does JS know which variables are accessible by any given statement
- how does it handle two variables of the same name?
But also:
- how does the JS engine process our program before it runs?
Compiled vs. Interpreted permalink
- 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 saved in an executable output (file).
- With interpretation, the source code is transformed line by line; each line or statement is executed before immediately proceeding to process the next line of the source code.
- Modern JS engines employ numerous variations of both compilation and interpretation in the handling of JS programs.
Compiling Code permalink
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
parsing: taking a stream (array) of tokens and turning it into a tree of nested elements, which collectively represent the grammatical structure of the program (aka Abstract Syntax Tree or AST)
code generation: taking an AST and turning it into executable code.
🤔 Oi, but what is the difference between tokenizing and lexing?
- The difference lies in whether the tokens are identified in a stateless or stateful way. 😅 (this answer needs work)
Required: Two Phases permalink
- (at least) two phases of parsing/compilation first, then execution.
- The parsing behavior is observed in syntax errors and hoisting.
Syntax errors permalink
JS engine first parsing the entires program before any of it is executed and throws syntax errors if something isn't right.
Hoisting permalink
function saySomething() {
var greeting = "Hello";
{
greeting = "Howdy"; // error comes from here
let greeting = "Hi"; // because let lands you in the TDZ
console.log(greeting);
}
}
saySomething();
// ReferenceError: Cannot access 'greeting' before
// initialization
Now for the compilation bit: it makes sense that once JS is converted into AST (previous step), it is then compiled into its most efficient (binary) representation for the engine to execute.
Compiler Speak permalink
- Other than declarations, all occurrences of variables/identifiers in a program serve in one of two "roles": either they're the target of an assignment or they're the source of a value.
- Rule of thumb: is a value being assigned to a
variable ? target : source
Targets permalink
students = []; // students
for (let student of students) { // student
getStudentName(studentId) // studentId
function getStudentName(studentID) { // getStudentName
Sources permalink
for (let student of students) { // students
if (student.id == studentID) // both student and studentID
return student.name;
getStudentName(73) // getStudentName
console.log(nextStudent) // both console and nextStudent
Lexical Scope permalink
- Previously 🍿: lexical scope means determining the scope during (the lexing state) compile-time.
- 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.
- Also! 💡 if a variable is block-scope declared (
let
andconst
), then it's associated with the nearest enclosing{ .. }
block, rather than its enclosing function (as withvar
). - if a variable is not declared in the current scope, the next outer/enclosing scope will be consulted (until you reach
global
or find nothing - akaundeclared
). - Memory doesn't get allocated until the code is executed.
- During compilation, two things happen:
- scopes (lexical environments) get defined,
- identifiers (variables) are registered.