Table Of Content
3. Hoisting in JavaScript
4. Scope, Scope chain and Lexical Environment
1. Intro
JavaScript --> JavaScript is a single-threaded, synchronous language.
- Synchronous : In a specific order
- Single-threaded : One command at a time
JavaScript is a high-level, interpreted programming language primarily used for creating dynamic and interactive content on websites. It requires a runtime environment to run in browsers. Most famous runtime environment is V8 engine(google).
JavaScript can also be used in server-side(Node.js), which also uses V8 engine and some additional features to make it run outside the browsers.
2. Execution Context
Execution Context --> It refers to the environment inside which the javascript code is executed.
Execution context is created in two phases :-
- Variable Environment (Memory Component)
- Thread of Execution (Code Componeent)
i. Variable Environment
This is the first phase of javascript code execution. In this phase memory allocation takes place for variables and functions.
It goes through the code line by line and allocates memory :-
- For variables, it stores undefined as placeholder
- For functions, it stores the entire code.
var a = 2;
var b = "hi";
function printHello(){
console.log("hello world");
}
/*
For first phase it stores ----
**** GLOBAL EXECUTION CONTEXT ****
a : undefined
b : undefined
printHello() : the whole code is saved
*/
ii. Thread of Execution.
In this phase, code is executed line-by-line.
For variable, when initialisation is encountered it replaces the undefined with value.
When function call is encountered.
- A new execution context is created inside the previous one.
- When return is encountered then returns the control to the previous execution context.
var a = 2;
var b = "hi";
function printHello(){
var c = 11;
console.log("hello world");
}
// for below function call, a new exection context is created
// which also goes through the same two phases as we have discussed
printHello();
/*
For the second phase
*** GLOBAL EXECUTION CONTEXT ***
a : 2
b : "hi"
printHello() : code is executed
*/
Now, that we have understood how execution context is created and how function can create it's own execution context, you might be wondering how javascript handles all these execution context. The answer is call stack.
Call stack --> It is a simple stack of execution context with the top element being the function currently being executed.
For each function call javascript creates a seperate execution context and pushes it inside the call stack. The very first execution context which is pushed inside the call stack is global execution context.
In the first phase of execution context only memory allocation for variables and functions. No code execution during this phase
3. Hoisting in JavaScript
Hoisting --> It is a concept which allows us to extract values of variables and call function even before they are initialised.
Let us understand this concept with the help of example :
console.log(a); // undefined
hello(); // hello
var a = 10;
function hello(){
console.log("hello");
}
You might be wondering, how we can print variable a or how we could call function hello. We have already discussed the answer in the previous section.
During the first phase of memory allocation, the variable a was given value undefined and for function hello the whole code was saved. So, even before their declaration we can access them.
But there are some special case :-
- For variables declared using let or const, we can't access the variables before their declaration. The time between their hoisting and declaration is called Temporal Dead Zone(TDZ). Accessing any variable in their TDZ, will give us Reference Error.
console.log(a); // Reference Error
console.log(b); // Reference Error
let a = 10;
const b = 20;
- For functions expression, they are treated as variables before their declaration and only as function after their declaration.
console.log(fun1);
// OUTPUT - undefined
// What would would happen if we print fun2 and fun3 ?
// ans -> they are in their TDZ, so it would give Reference Error
fun1();
fun2();
fun3();
// for the above function calls it would give *Type Error*.
var fun1 = function(){
console.log('fun1');
}
let fun2 = function(){
console.log('fun2');
}
const fun3 = ()=>{
console.log('fun3');
}
4. Scope, Scope chain and Lexical Environment
Block --> Multiple lines of code grouped together using curly braces {} is called a block.
Scope --> Region of code inside which the variables and function can be accessed.
Javascript mainly has types of scope : local scope and global scope.
Variables declared using var are function scoped and variables declared using let or const are block scoped. It means that var variables follow the scope of function only and not any other block. Whereas, let or const variables can't be accessed outside of block they are declared in( also includes function block ).
// the below block could be any block
// *for*, *while* etc.
{
let a = 10;
const b = 11;
var c = 12;
}
console.log(a); // Reference Error
console.log(b); // Reference Error
console.log(c); // 12
// *c* can be accessed outside because it only follows
// function scope and here it is treated as if it was
// declared in globally
Lexical Environment --> The lexical environment is local memory along with lexical environment of it's parent. It consists of two components:
Environment Record: Stores the mapping of variable names to their corresponding values within the scope. For function scopes, it also includes a reference to the outer lexical environment.
Reference to the Outer Lexical Environment: A reference to the lexical environment in which the current lexical environment was created, forming a chain of lexical environments known as the scope chain.
Scope Chain --> The scope chain is a mechanism used to resolve references to variables within nested functions. When a variable is referenced within a function, JavaScript first looks for that variable within the local scope of the function. If it's not found there, it continues to look in the next outer scope, forming a chain until it reaches the global scope. This process is called scope chain traversal.
let a = "global a";
function outer(){
let b = "outer b";
let c = "outer c"
function inner(){
let b = "inner b";
console.log(a); // global a
console.log(b); // inner b
console.log(c); // outer c
}
inner();
}
outer();
// Lexical Chain
// NULL <- global <- outer <- inner
Top comments (0)