In this article, we are going to learn 4 important concepts of javascript- scope (in-depth), Single threading , call stack, and hoisting which are most common question asked in an interview.
What is Scope?
Scope is a very important concept that manages the availability of variables. It defines which parts of the program have access to variable or where the variable is visible
Types of Scope
There are three types of scope in javascript.
- Global scope
- Function scope
- Block scope
Global scope
Any variable that's not inside any block
or function is inside the global scope
.The variables in global scope
can be accessed from anywhere in the program.
For example:-
var learncodeonline= "hub of learning technology"
function lco(){
console.log(learncodeonline);
}
lco();
Here, we can see that learncodeonline
variable is inside the global scope and can be used anywhere whether its the function or method.
Local scope or function scope
The variables which are described inside a function is inside the local scope
.They can only be accessed within that function means they can't be accessed outside that function.
For example
function lco(){
var learncodeonline="hub of learning technology"
console.log(learncodeonline);
}
lco();// prints the variable
console.log(learncodeonline);// reference error:-learncodeonline is not defined
Block scope
ES6 introduced let
and const
variables unlike var
variables ,they can be scoped to nearest pair of curly braces.
That means they can't be accessed from outside that pair of curly braces.
For example:-
{
let learncodeonline="Hub of learning technology";
var lang="English";
console.log(learncodeonline); //Prints variable
}
console.log(lang); //Prints language
console.log(learncodeonline); // Uncaught referanceerror :-learncodeonline is not defined
In this example , we can see that var
can be used outside the block scope for that we can say that var
variables are not block scoped
.
Lexical Scope
Lexical scope
also known as static scoping is a convention used with many modern programming languages. It is the ability for a function scope to access variables from its parent scope.
In simple words, javascript looks for variables in a way , if it can't find a variable in a local Execution context
,it will look for it in a calling context
.And if it didn't find it in its calling context
. Repeatedly, until it's looking in the global Execution context
. and if it doesn't find it will return undefined
.
So lexical scoping
means that the scope is defined at the location where the variable is defined and not where they run.
let userName="Muskan";
function printUserName(){
console.log(userName);
}
function printUsernameAgain(){
let userName="Hitesh sir";
printUserName();
}
printUserNameAgain();
Let's understand what will happen here .
When the printUserNameAgain
is called it creates a local variable userName
and sets the value as Hitesh sir
. In the next line the function printUserName
is called and it is defined outside of printUserNameAgain
function. printUserName
function doesn't have the userName
variable so we have to go one scope to the global scope to get the value of the userName
which is Muskan
.
Even though we have value Hitesh sir
above the line where the function printUserName
is invoked we have never access it . This is because lexical scoping requires us to go where the functions are defined not where they are run . Hope the concept is clear .
Let's move to the scope chain
Scope chain
The scope chain is how javascript looks for the variable . However searching will be in lexical manner .First it will look for the current scope of current function if it does not find then it will find in the parent scope . If not there , it will look in the global scope and if noty find it declares the vraiable value as undefined.
for example:-
var username= "Muskan";
function first(){
var usernameone= " Hitesh sir ";
second();
function second(){
var usernametwo= "Anurag sir";
console.log(`I am ${username} and I learn from ${usernameone} and ${usernametwo}`);
}
}
first();
In this example , there are three functions and three scopes i.e global scope ,parent scope and child scope . In the first
function we call the second
function and where we have used the variable username
first it will look in current function second
but it is not there then it will go to it's parent not finding there , now it will go to its local scope and finally found the username
variable . Now after finding this we will find value of usernameone
and using same procedure we will be able to find inparent scope
and then search for third one usernametwo
which is present in local scope.
Javascript is single-threaded
Js is single threaded which means it will execute one statement at one time.
Before we deep dive into what single thread
exactly means and why javascript is single threaded we will first go over its terminology.
We will learn in best simplest manner possible.
Synchronous execution refers to code executing in sequence line by line , one line at a time. Whenever a function is called the program waits for the function to execute and returns and then only it moves to the next line.
const userone=>{
const usertwo =>{
console.log("wait");
}
usertwo();
}
We can see the call stack here it uses stack data structure - LIFO(Last In First Out). Its job is to insert the instructions and pop it after execution.
Single threaded means it has only one call stack . so it will execute first whatever presents on the top first. In this program , functions will run sequentially. The javascript V8 engine has two parts :-
- memory heap
- call stack
memory heap - It is used to allocate memory to all variables and functions .
call stack - Within the call stack the code is read and executed line by line.
Javascript is single threaded language which is advantageous as we don't have to deal with complicated scenarios like deadlock.
Since js is single threaded it is synchronous in nature .So you all might be wondering that how we are able to make async calls then ?
So let me explain you that how we can make async calls even if it is single threaded.
As we know that in synchronous calls all work is done line by line. It leads to wastage of time and resources .these two problems can only be overcome by asynchronous calls where one doesn't wait for one to complete instead it runs another task simultaneously.
So whenever we have to make API calls and processing we use async calls.
A good example we can say that is window.alert("hello");
until and unless you pressed Ok everything freezes and stuck.
Within js, we have lexical environment
, an execution context
that is used to execute the code . But beside this browser has event loop, a callback queue and a web api for execution.
However javascript can also be non-blocking
and behave as if it is multithreaded.
it is possible because of js engines which use real muti-threading langauages like c++(chrome ) or rust (firefox). They provide us the web API under node.js.
Lets look out an example:
console.log("1");
setTimeout (() =>{
console.log("2");
},1000);
console.log("3");
we can see 1 then 3 and then 2 with a brief delay of 1 sec. But why this is happening? In javascript, all instructions are put on call stack and when stack arrives at setTimeout the engine sees it as a web API instruction and pops it out and sends it to web api .Once it is done with execution , it will arrive at the callback queue. We will learn more about it in our asynchronous article.
Call stack
A call stack
is a mechanism for javascript interpreter in a web browser to keep track of all the function calls.
It is real stack data structure that follows LIFO- (Last In First Out) means the elements that are on top executed first.
Whenever we are executing a script , the js engine creates a global execution context
and pushes it on the top of call stack.
Whenever a function is called it will create a function execution context
and pushes it on the top of call stack , and startes executing that function
.
If that function calls another function the js engine creates a new execution context
for that function and again pushes it to the top of call stack .
Whenever the function completes the js engines pops out the top function an resumes the execution where it was left off.
For example:-
function add(a,b){
return a+b;
}
function average(a,b){
return add(a,b)/2;
}
let marks= average(10,20);
When the js engine starts executing it places the global execution context
on the call stack . Now the js engine executes the call to average(10,20) function and pushes to the top of call stack the average
function calls the add
function . At this point the js engine
creates another execution context on the top of call stack .
Now at first it will execute add
function and pops it off . Sameprocedure is going for the average
function . now the call stack is empty and so the program stops executing.
Hoisting
Hoisting is a process of enabling us to access the variables and functions even before they are initialised. This is happening due to first phase memort creation phase of execution context.
In javascript , Hoisting is the default behaviour of moving all the declarations at the top of scope before execution of code.
It allows us to call the functions even before writing them in the code.
Javascript only hosts declarations not the initializations.
Variable hoisting
Variable hoisting means that the javascript engines moves all the variable declarations
to the top of script .
console.log(number); // undefined
var number=20;
however the first line of code does not cause an error due to the concept of hositing .
Technically what the code will look like in execution phase
var number;
console.log(number);
number=20;
During the creation phase the global execution context places all the vraiable in memory and initializes it with undefined.
Using let keyword
console.log(number);
let number=20;
It will produce an error Reference Error : cannot access 'number' before initialization.
The error message shows us thet the
numbervariable can't be accessed but it is present in heap memory.
Behind the scenes, the js engine hoists variable declarations that use
let` keyword but doesn't initialize it.
Function hoisting
Like variable hoisting , the js engine also hoists the functions . It will move the function declarations to the top of the script . For example:-
let x = 5,y = 6;
let result = multiply(x, y);
console.log(result); // π 30
function multiply(a, b) {
return a * b;
}
here we can see that we are calling the function
before writing it .
technically what the code looks like in execution context:-
function multiply(a, b) {
return a * b;
}
let x = 5,y = 6;
let result = multiply(x, y);
console.log(result); // π 30
During the creation phase of the execution context, the JavaScript engine places the multiply() function declaration in the heap memory. To be precise, the JavaScript engine creates an object of the Function type and a function reference called multiply that refers to the function object.
Arrow functions The following example changes the multiply function to the arrow function:
let x = 5,
y = 6;
let result = multiply(x,y); // Uncaught ReferenceError: multiply is not defined
console.log(result);
let multiply = (x, y) => x * y;
During the creation phase of the global execution context, the JavaScript engine creates the multiply variable in the memory and initializes its value to undefined.
When executing the following code, the multiply is undefined, hence, it isnβt a function:
let result = multiply(x,y);
The multiply variable is assigned to an anonymous function only during the execution phase of the global execution context.
Code issues the reference error so we can say that arrow functions are not hoisted.
Conclusion
In this article , we have learned the important concepts of javascript that are most asked in an interview and difficult to understand . Hope after reading this article, we will not be confused in any of them and can answer by giving explanations and examples. if you like it please share your feedback. Thankyou for spending your precious time.