Scope, Thread, Call Stack, and Hoisting in JavaScript🚀

Scope, Thread, Call Stack, and Hoisting in JavaScript🚀

·

6 min read

Overview

In this article, we will understand four different topics in javascript. We will learn about scope in javascript, javascript execution as a single-threaded language, how the call stack works in javascript, and the most important topic of hoisting in javascript. Let's get started 🚀🚀.

Scope

The scope is the current context of execution in which values and expressions are "visible" or can be referenced. If a variable or expression is not in the current scope, it will not be available for use. Scopes can also be layered in a hierarchy, so that child scopes have access to parent scopes, but not vice versa.

JavaScript has the following kinds of scopes:

Global scope

Variables defined outside any function, block, or module scope have global scope. Variables in the global scope can be accessed from everywhere in the application.

Global variables are available for the lifetime of the application.

carbon.png

Another way for creating a global variable is to use the window global object anywhere in the application.

carbon (3).png

At this point, the GLOBAL_DATA variable is visible everywhere. Although it is a bad practice to declare the variables like this as GLOBAL_DATA.

Module scope

Before modules, a variable declared outside any function was a global variable. In modules, a variable declared outside any function is hidden and not available to other modules unless it is explicitly exported.

Exporting makes a function or object available to other modules.

carbon (4).png

Importing makes a function or object, from other modules, available to the current module.

carbon (5).png

In a way, we can imagine a module as a self-executing function that takes the import data as inputs and returns the export data.

Function scope

Function scope means that parameters and variables defined in a function are visible everywhere within the function, but are not visible outside of the function.

carbon (2).png

Block scope

Block scope is defined with curly braces. It is separated by { and }.

Variables declared with let and const can have block scope. They can only be accessed in the block in which they are defined.

carbon (6).png

Single Thread execution in JavaScript

JavaScript is a single-threaded language because while running code on a single thread, it can be really easy to implement as we don’t have to deal with the complicated scenarios that arise in the multi-threaded environment like a deadlock.

Since JavaScript is a single-threaded language, it is synchronous in nature. Now, you will wonder if you have used async calls in JavaScript so is it possible then?

So, let me explain to you the concept of async calls within JavaScript and how it is possible with single-threaded language. Before explaining it you let’s discuss briefly why we require the async call or asynchronous calls.

As we know within the synchronous calls, all the work is done line by line i.e. the first task is executed then the second task is executed, no matter how much time one task will take. This arises the problem of time wastage as well as resource wastage. These two problems are overcome by asynchronous calls, where one doesn’t wait for the one call to complete instead it runs another task simultaneously.

So, when we have to do things like image processing or making requests over the network like API calls, we use async calls.

carbon (8).png

Untitled Diagram.png

Within JS we have a lexical environment, syntax parser, and an execution context (memory heap and call stack) that is used to execute the JS code. But except these browsers also have Event Loops, Callback queue, and WebAPIs that are also used to run the JS code. Although these are not part of JS it also helps to execute the JS properly as we sometimes used the browser functions within the JS.

As you can see in the above diagram, DOM, AJAX, and Timeout are not part of JavaScript but part of RunTime Environment or browser, so these can be run asynchronously within the WebAPI using the callback queue and again put in the call stack using event loop to execute.

Call Stack in JavaScript

The call stack is used by JavaScript to keep track of multiple function calls. It is like a real stack in data structures where data can be pushed and popped and follows the Last In First Out (LIFO) principle. We use call stack for memorizing which function is running right now.

Let's take an example with this JavaScript code

carbon (9).png

Step 1: When the code loads in memory, the global execution context gets pushed into the stack.

Untitled Diagram1.png

Step 2: The secondFunction() function gets called, and the execution context of f2() gets pushed into the stack.

Step 3: The execution of secondFunction() starts and during its execution, the firstFunction() function gets called inside the secondFunction() function. This causes the execution context of firstFunction() to get pushed into the call stack.

Step 4: Now the firstFunction() function starts executing. A new stack frame of the console.log() method will be pushed to the stack.

Step 5: When the console.log() method runs, it will print Hi by firstFunction function! and then it will be popped from the stack. The execution context go will back to the function and now there is not any line of code that remains in the firstFunction() function, as a result, it will also be popped from the call stack.

Step 6: This will similarly happen with the console.log() method that prints the line Hi by secondFunction function! and then finally the function secondFunction() would finish and would be pushed off the stack.

Hoisting in JavaScript

Hoisting in JavaScript is a behavior in which a function or a variable can be used before declaration. For example,

carbon (10).png

Hoisting is the default behavior of moving all the declarations at the top of the scope before code execution. It gives us an advantage that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is global or local.

JavaScript only hoists declarations, not initialization.

Variable hoisting with var

When the interpreter hoists a variable declared with var, it initializes its value to undefined. The first line of code below will output undefined:

carbon (14).png

Hoisting comes from the interpreter splitting variable declaration and assignment.

Variable hoisting with let and const

Variables declared with let and const are hoisted but not initialized with a default value. Accessing a let or const variable before it's declared will result in a ReferenceError:

carbon (11).png

The reason that we get a reference error when we try to access a let or const variable before its declaration is because of the temporal dead zone (TDZ).

The TDZ starts at the beginning of the variable's enclosing scope and ends when it is declared. Accessing the variable in this TDZ throws a ReferenceError.

Function hoisting in JavaScript

Function declarations are hoisted, too. Function hoisting allows us to call a function before it is defined. For example, the following code runs successfully and outputs HoistedFunction:

carbon (12).png

Note that only function declarations are hoisted, not function expressions.

If we try to call the variable that the function expression was assigned to, we will get a TypeError or ReferenceError, depending on the variable's scope:

carbon (13).png

Conclusion

I hope you liked the article. If you liked it, give it a thumbs up. The above article explains Scopes, Call Stack, Thread, and Hoisting in Javascript briefly and how they work under the hood of javascript. Thanks for reading 🚀.

Â