What is JavaScript lexical scope?
Fortune Ekeruo
Introduction
JavaScript scope is basically the accessibility of variables or values within specific sections of your code during its execution. This is to provide security, reduce errors and make your code predictable, which is a principle applied to most programming paradigms (aka Principle of Least Privilege).
There are two types of scope in JavaScript:
- Global Scope
- Block/Function Level Scope
Global Scope
Just as the name implies, values or variables declared within this scope are available throughout the code and can be accessed anywhere.
See an example below
let name = 'Fortune';
let age = 22;
function func() {
// Both name and age can be accessed here
console.log(`The name is ${name}, and I'm ${age} years old`);
}
func(); // -> 'The name is Fortune, and I'm 22 years old'
The code above shows that the variables name and age are declared in the global scope, which makes them accessible everywhere including inside the function called func
.
The global scope can be the window or document object of your browser, it can also be the process object on a Node.js environment.
Block/Function Level Scope
This is also called the local scope, values or variables declared within a function cannot be accessed outside that function.
Even though functions can access variables in their outer scope, the reverse is not the case. Any value or variable that is not declared within a function is a global variable and can be accessed anywhere in that program file.
Example
let name = 'Fortune';
function func() {
let age = 22;
console.log(name, age);
}
func(); // 'Fortune 22'
// Accessing age here throws
console.log(age); // Uncaught ReferenceError: age is not defined
/**
* Variable declared without the var, let or const keyword
* gets thrown into the function's outer scope
*/
function withoutVarLetOrConst() {
out = 'Acessible within the outer scope';
}
withoutVarLetOrConst();
console.log(out); // -> 'Accessible within the outer scope'
// Variable "name" can be changed within a function scope
function changeName() {
name = 'Rose';
}
// Calling the function modifies the global variable "name"
changeName();
console.log(name); // -> 'Rose'
The variable age was declared within the func
function which means it cannot be accessed anywhere outside that function.
However, it’s worth noting that variables declared within a function without the var
, let
or const
keywords becomes accessible from the outer scope when that function is called as seen with the out
variable above.
Also, global variable values defined without the const
keyword can be changed within a local scope by just reassigning that variable, as shown in the function changeName
which changes the value of the global variable name
.
Most developers consider setting variables within the global scope as a bad practice as it leads to confusions and makes fixing bugs difficult.
Block Statements
Blocks statements are if
, switch
, for
and while
loops, variables declared within them using the var
can be accessed from their outer scope. When I create a variable with the var
keyword within a block statement such as if
, then that variable is also accessible from the outer scope.
A block statement is not a scope but if a variable is declared with the ES6 let
or const
keywords, then it's only accessible within that block statement.
Example
for (var i = 1; i <= 5; i++) {
console.log('Number: ' + i);
}
// variable i is accessible here
console.log(i); // -> 6 - which is the last value of i incremented by 1
/**
* However, using the ES6 let or const keyword to declare the variable
* makes it behave like a scope as it's only accessible within the for
* loop block.
*/
for (let x = 1; x <= 5; x++) {
console.log('Number: ' + x);
}
// Access variable j outside the block throws
console.log(x); // -> ReferenceError: x is not defined
The variable i
defined with the var
keyword within the first for
loop block is accessible within its surrounding scope (The global scope in this case), but using the let
or const
keyword prevents that as seen in the second for
loop block.
Hoisting
Consider the code below
console.log(age); // -> undefined
var age = 22;
console.log(age); // -> 22
console.log(location); // throws -> Uncaught ReferenceError: location is not defined
location = 'Lagos, Nigeria';
I tried to log age
before assigning it to a value but I got undefined
and not an error as you'd expect. This is so because when a variable is declared with the var
keyword then that variable is automatically initialised to undefined
and by default hoisted to the top of its available scope.
The JavaScript engine sort of restructures the code behind the scenes before execution, which mean it first declares the variables right before they're assigned a value.
However, variables declared without the var
, let
or const
keywords are not hoisted as is the case with the location
variable in the example code above.
On the other hand, functions are hoisted a little differently from variables.
Consider the code below.
console.log(func1);
console.log(func2);
let func1 = function () {
// do something
};
function func2() {
// do another thing
}
The first function func1
is initialized to undefined
just as expected but func2
is hoisted in its entirety, which means that it’s completely accessible right at the top unlike func1
which is only but undefined
at the top.
In a given Javascript turn, functions are initialized first before variables, the JavaScript engine parses all the syntax, allocates memory to the functions first then the variables. Hence, making func2
accessible before it was defined.
Conclusion
Understanding how the JavaScripts scope works will improve the efficiency of your code and help you track bugs easily. I really hope this article has helped you understand it much better.
If you spot something that needs correction or better explanation, please tweet me @fortune_ik.
Checkout my article on how the concept of this
keyword works.
Happy Coding!