Scope
What is scope?
In simple terms, scope is nothing but a piece of area in our code where a variable is accessible to us.
Types of scope:
Global scope
Block scope
Function scope or local scope
Let's go through each of this type.
Global Scope: Variables declared in global scope are available throughout anywhere in the code.
var attribute ='test';
function printTest(){
console.log(attribute ); //test
}
printTest();
console.log(attribute ); //test
If we examine the code execution in browser, we can see that a global scope is created and attribute variable is available in it. This is available till the end of file execution(total code) and then vanishes.
Block Scope: Variables declared in block level { } are available only in the block. They vanish after the execution of block and are not accessible outside the block.
var a = 2;
if(a<5){
let b=a-1;
console.log(b); //1
}
console.log(b); //ReferenceError: b is not defined
console.log(a) //2
In the above example, b is not accessible outside 'if' block. But a, which is in global scope, is available inside the 'if' block.
Note: Block scope applies for both Let and Const variables but not for variables declared with var. Before ES6 there is no block scope concept. All the variables declared with 'var' are available in global scope. In ES6, new keywords like let and const were introduced and the block scope came into the picture.
var a = 2;
if(a<5){
var b=a-1;
console.log(b); //1
}
console.log(b); //1
console.log(a) //2
Since Var variable declared inside the block is available in global scope, it is accessible outside the block scope.
Function Scope (or) Local Scope: Variables declared in functions are available only in the function block. They vanish after the execution of function and are not accessible outside the function.
function funScope() {
var a="apple";
console.log(a);//apple
}
console.log(a);//ReferenceError: a is not defined
Variable declared in function is available in local scope.
After the execution of function there is no local scope, it gets vanished.
Call Stack
When we execute a javascript program JS engine will create a call stack and it works on FILO(first-in last-out) principle. During the execution, GEC global execution context is pushed into call stack where execution happens. If any function is invoked, it(FEC function execution context) is pushed into the stack and popped after completing its execution. This process occurs sequentially.
consider below example:
let a='apple';
console.log(a);
function sample() {
var c=90;
console.log(c); //90
function innerSample() {
var e=c;
console.log(e); //90
}
innerSample();
}
sample();
function sample2(){
console.log("sample2 function");//sample2 function
}
sample2();
Let's us see diagrammatically how functions are stacked into call stack.
Initially GEC is created and pushed into call stack for code execution.
Whenever a function comes into picture FEC is created and pushed into call stack. Hence when
sample()
function is invoked, it is moved into call stack and executes the code.-
Inside sample function we are invoking innerSample() function. Now innerSample function is pushed into call stack and code gets executed.
After
innerSample()
line there is no code to execute in sample function. Now thesample()
function is popped out from call stack.Again
sample2()
invokes sample 2 function and it is moved to call stack and popped out after execution.After executing all lines GEC is popped out of stack.
Javascript is Single threaded or multi-threaded ?
Javascript is a single-threaded language because it has only one call stack which executes functions sequentially. So at each instance JS engine can run only one process. It cannot run multiple process at a single instance.
Asynchronous JavaScript
If JS Engine has only one call stack, how does the asynchronous tasks executed in browser?
Asynchronous behaviour can be achieved by either using promises or WEB Apis like setTimeout(), fetch() provided by web browsers through window object.
Let us consider setTimeout api. In below code, callback function inside setTimeout is executed last.
console.log('Start');
setTimeout(()=>{
console.log('Executing after 5000ms');
},5000);
console.log('End');
output:
Start
End
Executing after 5000ms
GEC is pushed into call stack and execution starts. when an asynchronous task is encountered, browsers take care of these outside call stack, allowing the call stack to proceed further executing remaining code.
Once the async tasks are completed callbacks need to executed. In our case, after 5 seconds callback need to be executed but it is passed to some intermediate place called Callback queue
. This queue is monitored by event loop - which checks whether call stack is available for execution of callback function and the moment call stack goes empty, event loop will pop the callback function from this queue and pushes it into call stack for execution.
All the callback functions coming from promises and Mutation Observers are sent to something known as microtask queue
and has higher priority than callback queue.
Hence Event loop
monitors call stack, callback queue and microtask queue. Whenever call stack is empty, callback functions are sent to call stack according to the priority from queues.
Hoisting
In Javascript we can use functions and variables before declaration this concept is called hoisting.
This is possible because of execution context which scans the javascript code before its execution and declares all the variables as undefined and function references are stored. Since variables/functions are already scanned before their declaration we are able to access them.
console.log(a);//undefined
var a=5;
example();
function example(){
console.log('example'); //example
}
Even before executing var a=5;
we can see a
is declared and value is undefined.
Also we can see the example
function reference.
- Hoisting is possible only for var variables. This hoisting cannot be achieved using const and let. Let and const are not defined in global scope instead they are available in separate scope called script. Since they are in separate scope they are not accessible before declaration.
console.log(a);//ReferenceError: Cannot access 'a' before initialization
let a=5;
Hoisting during assigning a function to variable
We know that var variables are scanned before the execution and assigned undefined value.
That's the reason
hoistExample
is undefined.But after it is assigned with function we can see its printing function reference.
console.log(hoistExample);//undefined
var hoistExample=function(){
console.log('hoisting test')
}
console.log(hoistExample);//[Function: hoistExample]
Similarly guess what will be output when we use let & const instead of var for assigning functions.
🙏Thanks for reading my blog✍️.
Happy coding journey🙂