Pillar 1: Scope and Closure
The organization of variables into units of scope (functions, blocks) is one of the most foundational characteristics of any language; perhaps no other characteristic has a greater impact on how programs behave.
Scopes are like buckets, and variables are like marbles you put into those buckets. The scope model of a language is like the rules that help you determine which color marbles go in which matching-color buckets.
Scopes nest inside each other, and for any given expression or statement, only variables at that level of scope nesting, or in higher/outer scopes, are accessible; variables from lower/inner scopes are hidden and inaccessible.
This is how scopes behave in most languages, which is called lexical scope. The scope unit boundaries, and how variables are organized in them, is determined at the time the program is parsed (compiled). In other words, it’s an author-time decision: where you locate a function/scope in the program determines what the scope structure of that part of the program will be.
JS is lexically scoped, though many claim it isn’t, because of two particular characteristics of its model that are not present in other lexically scoped languages.
The first is commonly called hoisting: when all variables declared anywhere in a scope are treated as if they’re declared at the beginning of the scope. The other is that var
-declared variables are function scoped, even if they appear inside a block.
Neither hoisting nor function-scoped var
are sufficient to back the claim that JS is not lexically scoped. let
/const
declarations have a peculiar error behavior called the “Temporal Dead Zone” (TDZ) which results in observable but unusable variables. Though TDZ can be strange to encounter, it’s also not an invalidation of lexical scoping. All of these are just unique parts of the language that should be learned and understood by all JS developers.
Closure is a natural result of lexical scope when the language has functions as first-class values, as JS does. When a function makes reference to variables from an outer scope, and that function is passed around as a value and executed in other scopes, it maintains access to its original scope variables; this is closure.
Across all of programming, but especially in JS, closure drives many of the most important programming patterns, including modules. As I see it, modules are as with the grain as you can get, when it comes to code organization in JS.
To dig further into scope, closures, and how modules work, read Book 2, Scope & Closures.