Scopes and Variable Shadowing in JS
If I give you the following example code:
const age = 30;
function func() {
const age = 31;
console.log(age);
for(let i = 0; i < 2; i++) {
const age = 32;
console.log(age);
}
}
func();
console.log(age);
What will be printed to the console?
Try to answer before you read on :) Self- testing is a great way of acquiring knowledge.
JavaScript is not terribly difficult but there’re nuances that are not always visible at the first glance.
What I want to demonstrate in the example above are scopes and variable shadowing.
There are 3 scopes in the example above — global, function, and block. JS has nowadays these 3 scopes. When I say scope, it basically means visibility — scope means where your declared variables will be visible.
For example, I can define a variable in a function:
function foo() {
const variable = 5;
}
I can use the variable name inside this function foo()
— the variable is visible inside this function. If I try to use the name outside, I’ll get ReferenceError
.
On the other hand, if I try to declare the same variable again in the same scope, I’ll get SyntaxError
telling me that the identifier is already declared.
Notice how I said “in the same scope” in the previous sentence. Because I can do the following:
const variable = 5;
function foo() {
const variable = 6;
}
And there’ll be no error at all.
This is where shadowing comes into play. variable
inside the foo()
function shadows variable
in the global scope in this example.
It also means that referring to variable
inside the function foo()
will yield 6. Referring to variable
in the global scope will yield 5.
Going back to the initial example, but with answers in comments:
const age = 30;
function func() {
const age = 31;
console.log(age); // 31
for(let i = 0; i < 2; i++) {
const age = 32;
console.log(age); // 32
}
}
func();
console.log(age); // 30
When run, it’ll produce this output:
31
32
32
30
One caveat with scopes might be when it comes to using the keyword var
when declaring variables. const
and let
are block scoped, which means that if I define variables with there keywords in a block, like in a for
loop, they will not leak outside the scope — I’d get ReferenceError
if I try to access them outside the scope where they were declared.
But when it comes to var
, such variables are not block scoped; they can be function scoped.
An example might be better:
function foo() {
var a = 1;
for(let i = 0; i < 2; i++) {
var b = i;
}
console.log(b); // 1
}
foo();
console.log(a); // ReferenceError
Notice how b
leaked outside the block scope but did not leak outside the function scope.
This is typically unwanted and a bit surprising, that’s why const
and let
are used more in modern JavaScript. But it’s good to know about var
as well to be aware of this risk.
If you like my posts, please follow me. Thank you.