Scoping in JavaScript Explained | JavaScript Interview Questions
JavaScript is one of the most popular languages. There are a lot of resources on the web teaching JavaScript and its concepts. Our aim with this series is to share a concise, useful, and quality list of questions with their answers. We don't want to add to the noise but rather be the source of clarity.
Explain Scope in JavaScript
In the most simple terms, scoping/scope is the area in which a variable is accessible. Scoping of a variable is controlled by the location of its declaration. Let us consider an example.
function init() {
var name = "Devtools Tech";
console.log(name); // logs Devtools Tech
}
console.log(name); // ReferenceError
In the above code snippet, variable name
is declared inside the function init
i.e. anywhere inside this function we can get or set the value of the variable. However, accessing the same variable outside the function would lead to ReferenceError: name is not defined
. Let us understand why this happens!
Types of Scoping
Block Scope
It is the scope created by a code block in JavaScript. Now, you must be wondering what is a code block
?
A (code) block is used to group zero or more statements i.e. any code written inside a pair of curly brackets.
if (true) {
// code block
let name = "Devtools Tech";
let link = "youtube.com/c/devtoolstech";
}
for (let i = 0; i < 4; i++) {
// code block
console.log(i);
}
Variables declared using let
and const
are block-scoped. They are not accessible outside the code block they are declared in. For example --
if (true) {
let name = "Devtools Tech";
let link = "youtube.com/c/devtoolstech";
// logs Devtools Tech youtube.com/c/devtoolstech
console.log(name, link);
}
console.log(name, link); // Reference Error
In the above code snippet, the variable name
and link
are only accessible inside the scope created by the if
statement. The second console.log(name, link)
throws a Reference Error
because variables are accessed outside of their scope i.e. the variables doesn't exist there.
The keywords if
, for
, while
create their respective block scope. In JavaScript, we can define standalone code blocks too.
{
// Block Scoped
let name = "Devtools Tech";
const link = "youtube.com/c/devtoolstech";
}
console.log(name, link); // Reference Error
We talked about how let
and const
are block-scoped variables. However, variables declared using var
are not limited to block scope but rather function-scoped. Let us understand more in the next section.
Function Scope
Any function in JavaScript defines a scope for variables declared inside it. The variables could be defined either using var
, let
, or const
.
function init() {
var name = "Devtools tech";
const link = "youtube.com/c/devtoolstech";
if (true) {
console.log(name, link); // logs the values
if (true) {
console.log(name, link); // logs the values
}
}
console.log(name, link); // logs the values
}
console.log(name, link); // ReferenceError
The variables are accessible anywhere inside the function, no matter how deep inside the function they are used. However, if we try to access the same variables outside the function then we would get the ReferenceError
as they simply don't exist there.
In some cases, function scope
can be considered as the parent scope of the block scope
with uni-directional scope access.
function init() {
// function scope
const name = "Devtools Tech";
if (true) {
// block scope
const link = "youtube.com/c/devtoolstech";
console.log(name, link); // logs both the values
}
console.log(name); // logs the value
console.log(link); // ReferenceError
}
In the above code snippet, the variable name
is declared in the function scope so it is accessible inside the code block created by the if
statement. However, vice versa is not true. The variable link
created inside the if
statement is block-scoped and cannot be accessed outside. Hence, function scope
is acting as the parent scope and you can only access variables in one direction i.e. top to bottom.
Curious Case of var keyword
The variables declared via var
bypass the above-mentioned rule. No matter how deep you declare a variable using var
. It is always scoped to the nearest function declaration i.e.
function init() {
if (true) {
if (true) {
if (true) {
if (true) {
if (true) {
var name = "Devtools Tech";
}
}
}
}
}
// logs Devtools Tech as name is scoped to the function `init`
console.log(name);
}
Global Scope
It is the outermost scope. Any variable defined in the global scope is accessible inside any other scope. In a browser environment, any file loaded using the script
tag is a global scope.
// file: videos.js
// global scoped variable
function getChannelLink() {
return "youtube.com/c/devtoolstech";
}
...
<script src="video.js"></script>
<script type="text/javascript">
// logs the value
// getChannelLink is accessible here because it is a global variable
console.log(getChannelLink());
</script>
Different JavaScript runtimes provide different global variables. The browser provides document
and window
global variables whereas the Node environment provides global variables like process
.
Lexical Scope
Let us first take a look at an example
// global scope
function analytics() {
// outer scope
const category = "Devtools Tech";
function track(action) {
// inner scope
window.ga("send", {
eventCategory: category,
eventAction: action,
});
}
return track;
}
const track = analytics();
track("Question Clicked");
In the above code snippet, the function track
invocation happens outside the analytics
function scope. Yet, it is still able to access and understand the category
variable that is defined inside the scope of the analytics
function.
This is due to lexical scoping. JavaScript implements a scoping mechanism called lexical/static scoping. It means that the accessibility of variables are determined by their position within the nested function scopes i.e.
- When the JavaScript engine checks for the
category
variable inside thetrack
function then it first checks its local scope. If found then use it. - If the variable is not found in the local scope then it checks the outer scope. If found then use it.
- If not found in the outer scope then it traverses upwards till it either finds the variable or reaches the global scope.
This is what is also known as closure
. The lexical scope of the track
function consists of the scope of the analytics
function.
Added benefits of Scoping
One direct benefit of scoping and using it well is that you can reuse variable names without conflicts.
function iterate(arr) {
const count = 0;
while (count !== arr.length) {
console.log(arr[count]);
count++;
}
}
function counter(limit) {
const count = 1;
while (count !== limit) {
console.log(count);
count++;
}
}
In the above code snippet, we are using the variable name count
in both functions. Since both variables are blocked scope, there won't be any conflict issues.
Final Thoughts
Understanding scoping is essential to write error-free code. It is a fundamental concept. Learning about different scopes and how they impact each other leads to more robust codebases and makes your life easier. I hope this post was useful to you. Do share it with others and help them too! :D