Block Scope & Shadowing in JavaScript
Block
Anything between two braces { } is called a block. It is also known as compound statement. A block is used to combine multiple JS statements into a single group. This allows us to have multiple statements in a place where JS expects a single statement.
if(i > 10) {
console.log("true");
i--;
}
In JavaScript, if statement expects a single statement. So, if we want to execute multiple lines of code within the if statement, we'll have to combine those statements into a single group by placing them inside a block.
Block Scope
Block scope refers to the functions and variables that can be accessed within a given block.
{
var a = 10;
const b = 20;
let c = 30;
console.log(a);
console.log(b);
console.log(c);
}
console.log(a);
console.log(b);
Output
10
20
30
10
Uncaught ReferenceError: b is not defined
From the above code, we can infer that let & const are block scoped, while var is not block scoped.
var is function scoped, i.e., var can be accessed anywhere in the lexical environment of the variable. Hence we cannot access let and const outside the block they're declared in.
let and const are not attached to the global object. Instead they are stored in a separate memory. But var gets attached to the global object.
Shadowing
When we have the same variable name in different places, the value of the variable closest to the line of execution is chosen. This phenomenon is called shadowing.
var a = 10;
const b = 20;
let c = 30;
{
var a = 100;
const b = 200;
let c = 300;
console.log(a);
console.log(b);
console.log(c);
}
console.log(a);
console.log(b);
console.log(c);
Output
100
200
300
100
20
30
The variables a, b and c inside the block are shadowing their counter parts having the same name outside the block.
But notice how in case of var, the shadowing is permanent.
This is because var is function scoped and gets attached to the global object. So a variable having the same name within a block and outside would point to the same location in the global object.
If we replace the block in the above code with a function, var behaves just like let and const.
var a = 10;
const b = 20;
let c = 30;
function x() {
var a = 100;
const b = 200;
let c = 300;
console.log(a);
console.log(b);
console.log(c);
}
x();
console.log(a);
console.log(b);
console.log(c);
Output
100
200
300
10
20
30
We don't see this in case of let and const as they're block scoped. JavaScript maintains a separate instance of them inside every block where they're declared.
Illegal Shadowing
The gist: while shadowing a variable, it should not cross the boundary of the scope.
#Illegal
let a = 10;
{
var a = 100;
}
Uncaught SyntaxError: Identifier 'a' has already been declared
This is illegal because var is not block scoped. So it put's JS in jeopardy whether to have 100 or 10 in the global space, and it can't just pick one. Also, in a particular scope, let cannot be re-declard. Hence the error.
#Legal
var a = 10;
{
let a = 100;
}
This is legal because let is block scoped. So outside the block JS doesn't have any other value other than 10 to choose from.
#Legal
let a = 10;
function x() {
var a = 100;
}
This is legal because var is function scoped. So JS once again has only one choice in the global space.