A relatively basic concept in JavaScript is that each declared function creates its own scope. What gets a little more mind bending is the concept of a closure - a function which is able to remember and access its lexical scope even when that function is executing outside its lexical scope.
Lexical scope is the scope model used by the JavaScript language, which differs to some other languages which use dynamic scope. Lexical scope is the scope defined at lexing time.
So, what is lexing time?
This digs into the mechanics of how JavaScript engine works. Despite commonly being referred to as an interpreted language, JavaScript compiles code immediately before executing it. For example the statement: var a = 2;
is split into two separate steps at lexing time:
var a
This declares the variable in the scope, before code execution.a = 2
This assigns the value 2 to the variable a, if it is found in the available scope.
The lexing phase of compilation determines where and how all identifiers are declared, and thus how they will be looked up during execution. This is the same mechanism which results in “hoisting” variables. The variables are not actually moved within the source code, the declarations simply occur during the lexing phase and so the JavaScript engine is aware of these before execution.
Consider these examples:
Example 1:
var a = 1; console.log('a:', a); // a: 1
Example 2:
console.log('a:', a); // a: undefined var a = 1;
Example 3:
console.log('a:', a); // Uncaught ReferenceError: a is not defined
Example 1 is straightforward and works as expected, however note the subtle difference between other two examples. Example 2 logs that the value of a is undefined, but the identifier a has itself been declared; compared with example 3 in which the identifier a has not been declared, hence resulting in a reference error.
This demonstrates that during the lexing phase, the JavaScript engine declares the variables first, before the following step in which the values are assigned to the identifiers - this is hoisting. Because functions are also defined at this time (lexing phase), we can say that lexical scope is based on where variables and blocks of scope exist at author time, and thus are locked down at the end of the lexing phase. Scope is not defined at runtime, rather it can be accessed at runtime.
Again, a closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.
function foo() { // 'scope of foo' aka lexical scope for bar var memory = 'hello closure'; return function bar() { console.log(memory); } } // returns the bar function and assigns it to the identifier 'closure’; const closure = foo(); closure(); // hello closure
So… lexical scope is the author-time scope created by a closure. It is the ‘outer’ scope of a function which is defined inside a closure.
function scope of outer function === lexical scope of inner function.
One Response to “What is Lexical Scope Anyway?”
Wow!! mind blown…this concise example cleared up so many nebulous things for me. Thanks!!