It's possible to make scopes inside of scopes. This may sound intimidating and certainly can make code behavior more difficult to interpret, but the way JavaScript handles nested scope is not so difficult to understand.
From JavaScirpt's perspective, there is no difference between a nested block and an un-nested block. Hopefully this example will help make that clear.
Source Code vs. Runtime Another concept this example clarifies is the difference between source code and runtime:
- In the source code (the JS you see printed on the screen), blocks 1 & 2 always exist. There will always be "let var_2 = [];" displayed on the screen no matter which line of code is being executed.
- Runtime state is dynamic, it changes depending on what line is being executed and what has happened before. "vari_2" only exists in memory for 4 steps of execution (lines 6, 8, 9, 10).
This example illustrates how JS treats nested scopes and let variables:
- The variables all point to arrays. The values stored in each array indicate in which scope that variable is currently visible.
- The console.log's will indicate how JavaScript is managing block scopes behind the scenes.
let vari_0 = ['global'];
console.log('preparing block 1');
{
let vari_1 = [];
vari_0.push('b1'), vari_1.push('b1');
console.log('preparing block 2');
{
let vari_2 = [];
vari_0.push('b2'), vari_1.push('b2'), vari_2.push('b2');
console.log('clearing block 2'), vari_0.pop(), vari_1.pop();
};
console.log('clearing block 1'), vari_0.pop();
};
console.log('final state');
variable names in the diagrams are different from the snippet: vari_x -> block_x behavior is the same
Very long/thorough example. (PythonTutor link):
let global_let = 'global_let';
var global_var = 'global_var';
{
let b_1_let = 'b_1_let';
var b_1_var = 'b_1_var';
{
let b_2_let = 'b_2_let';
var b_2_var = 'b_2_var';
global_let;
global_var;
b_1_let;
b_1_var;
b_2_let;
b_2_var;
};
global_let;
global_var;
b_1_let;
b_1_var;
// b_2_let; // -> ReferenceError: b_2_let is not defined
b_2_var;
};
global_let;
global_var;
// b_1_let; // -> ReferenceError: b_1_let is not defined
b_1_var;
// b_2_let; // -> ReferenceError: b_2_let is not defined
b_2_var;