Adv JavaScript

Two primary components of a JS engine: the Call Stack and the Memory Heap.

What is JavaScript Engine?

The definition of JavaScript engine is, it is a program that executes JavaScript code. For example, V8 (Chrome, Node.js, Edge), SpiderMonkey (Firefox), JavaScriptCore (Safari). It core job is to take your human-readable JavaScript code and translate it into machine-executable instructions. It is a simplified architecture we can start with two fundamental data structures that every engine has 

  1. The Memory Heap (Memory allocation happens)
  2. The Call Stack (Where code is executed, keeping track of where we are in the program)

What is Memory Heap?

Its primary purpose is to allocate memory dynamically. It is a largely unstructured pool of memory where objects, functions, and other large data structures are stored. The Heap is like a sprawling, free-form city. You can build houses (objects) anywhere there’s space. The city planning (memory layout) is not sequential; it’s dynamic and can get messy. A “memory manager” (like a city planner) finds free space for new allocations and cleans up garbage (Garbage Collection) from abandoned lots. 

The Associated Problem: Memory Leaks

  • If you keep references to objects you no longer need, the Garbage Collector cannot free up that memory.

  • Over time, the Heap grows unnecessarily, slowing down the application and potentially causing crashes.

Call Stack

Tracks the execution context of the currently running code. It’s a data structure that records “where we are” in the program.

It’s a stack (LIFO – Last-In, First-Out). When a function is invoked, a new frame (containing the function’s arguments and local variables) is pushed onto the top of the stack. When a function returns, its frame is popped off the top of the stack.

The Call Stack is like a stack of books. You can only interact with the book on the top. To get to a book in the middle, you must remove all the books on top of it. The book at the bottom was the first one placed there.

JavaScript has a single call stack, meaning it can only do one thing at a time. This is the source of JavaScript’s synchronous, single-threaded nature.

StepActionStack Contents (Top to Bottom)
1printSquare(4) is calledprintSquare frame
2printSquare calls square(4)square -> printSquare
3square calls multiply(4, 4)multiply -> square -> printSquare
4multiply returns 16square -> printSquare
5square returns 16printSquare
6console.log(16) is called *console.log -> printSquare
7console.log returns (undefined)printSquare
8printSquare returns<empty>

Tracks the execution context of the currently running code. It’s a data structure that records “where we are” in the program.

It’s a stack (LIFO – Last-In, First-Out). When a function is invoked, a new frame (containing the function’s arguments and local variables) is pushed onto the top of the stack. When a function returns, its frame is popped off the top of the stack.

The Call Stack is like a stack of books. You can only interact with the book on the top. To get to a book in the middle, you must remove all the books on top of it. The book at the bottom was the first one placed there.

JavaScript has a single call stack, meaning it can only do one thing at a time. This is the source of JavaScript’s synchronous, single-threaded nature.

StepActionStack Contents (Top to Bottom)
1printSquare(4) is calledprintSquare frame
2printSquare calls square(4)square -> printSquare
3square calls multiply(4, 4)multiply -> square -> printSquare
4multiply returns 16square -> printSquare
5square returns 16printSquare
6console.log(16) is called *console.log -> printSquare
7console.log returns (undefined)printSquare
8printSquare returns<empty>
What is Hoisting?

Hoisting is a direct consequence of the Creation Phase of an Execution Context. 

During this phase, the JavaScript engine:

  1. Scans the code for variable declarations (using var, let, const) and function declarations.

  2. For each declaration, it allocates memory and sets up a binding in the current Variable Environment (the scope).

  3. The key difference between var, let, and const lies in how this binding is initialized and when it becomes accessible.

var Hoisting:
  • Behavior: Variables declared with var are hoisted and auto-initialized to undefined.

  • Mechanism: During the Creation Phase, the JavaScript engine finds var myVar;, creates the binding in the variable environment, and immediately sets its value to undefined.

Example :

console.log(myVar); // Output: undefined (NOT a ReferenceError!) var myVar = 5; console.log(myVar); // Output: 5

Explanation: This code is interpreted by the engine as if it were: var myVar = undefined; // Creation Phase: Hoisted and initialized console.log(myVar); // Execution Phase: Logs `undefined` myVar = 5; // Execution Phase: Assignment console.log(myVar); // Execution Phase: Logs `5` Note: Accessing a var variable before its declaration line will yield undefined, which can be a major source of bugs. let and const Hoisting
  • Behavior: Variables declared with let and const are hoisted but are NOT initialized. They are unavailable until their declaration is encountered in the code.
  • The Temporal Dead Zone (TDZ): This is the critical concept. The TDZ is the region of code from the start of the block until the line where the variable is declared. Accessing the variable in this TDZ results in a ReferenceError.
Code Example(let): // Start of TDZ for ‘myLet’ console.log(myLet); // ❌ ReferenceError: Cannot access ‘myLet’ before initialization // End of TDZ for ‘myLet’ let myLet = 10; console.log(myLet); // Output: 10 Code Example(const): // Start of TDZ for ‘myConst’ console.log(myConst); // ❌ ReferenceError: Cannot access ‘myConst’ before initialization // End of TDZ for ‘myConst’ const myConst = 15; console.log(myConst); // Output: 15
Key Differences Summary Table
Feature var let const
Hoisted? Yes (and initialized to undefined) Yes (but uninitialized, in TDZ) Yes (but uninitialized, in TDZ)
Scope Function Scope (or Global) Block Scope ({}) Block Scope ({})
Re-declaration Allowed (in the same scope) ❌ Not allowed (SyntaxError) ❌ Not allowed (SyntaxError)
Re-assignment Allowed Allowed Not allowed (for primitives), but mutable object properties can change.
Initial Value undefined Optional (can be declared without one) Mandatory (must be assigned at declaration)
Global Property Creates a property on the window object Does not create a property on window Does not create a property on window
<script>
    for( var i=0; i<5; i++) {
        setTimeout(() => {
            document.write(i);
        } , 100);
    }
</script>

Step 1: Understanding the loop

  • The loop runs from i = 0 to i < 5.
  • So, iterations: i = 0, 1, 2, 3, 4.
  • Normally, we’d expect to print 01234.

Step 2: setTimeout behavior

  • setTimeout schedules the callback function (inside () => { document.write(i) }) to run later, after 100 ms.
  • The loop does not wait; it continues executing immediately and finishes before the timeout triggers.

Step 3: Variable scope with var

  • var is function-scoped, not block-scoped.
  • So there is only one shared i variable for all iterations.
  • By the time the setTimeout callback actually executes, the loop has already finished, and i has become 5.

Step 4: What gets printed

  • Each of the 5 setTimeout callbacks will run after ~100ms.
  • At that time, i = 5 for all of them.
  • So, instead of 01234, you get:
  • 55555
<script>
    for(let i=1; i<=5; i++){
        setTimeout(() => {
            document.write(‘<br>’+i);
        }, 100);
    }
</script>

Step 1: Loop execution

  • The loop runs from i = 1 to i = 5 (inclusive).
  • So iterations: 1, 2, 3, 4, 5.

Step 2: Role of let

  • Unlike var, let is block-scoped.
  • That means in each iteration of the loop, a new copy of i is created and preserved separately for that iteration.
  • So, each scheduled setTimeout callback keeps the correct value of i at the moment it was created.

Step 3: setTimeout scheduling

  • setTimeout(() => { document.write('<br>'+i); }, 100);
    schedules the printing of i after 100 milliseconds.
  • The loop finishes very quickly, scheduling all 5 timeouts almost instantly.
  • After ~100ms, all 5 callbacks execute almost at the same time.

Step 4: Final Output

  • Since each iteration has its own preserved i value (thanks to let):
1
2
3
4
5
<script>
    const emp = {name:’Suresh’}
    document.write(emp.name);
    emp = {name:’Ravi’}
    document.write(‘<br>’+emp.name)
    emp.name = ‘Ravi’
    document.write(‘<br>’+emp.name)
</script>
 
It shows an error, so correct the code by commenting…
 
<script>
    const emp = {name:’Suresh’}
    document.write(emp.name);
    //emp = {name:’Ravi’}
    //document.write(‘<br>’+emp.name)
    emp.name = ‘Ravi’
    document.write(‘<br>’+emp.name)
</script>

Let’s carefully analyze your code step by step:

<script>
    const emp = {name:'Suresh'}
    document.write(emp.name);

    emp = {name:'Ravi'}
    document.write('<br>'+emp.name)

    emp.name = 'Ravi'
    document.write('<br>'+emp.name)
</script>

Step 1: Declaration
const emp = { name: 'Suresh' };
  • Here emp is declared as a constant reference.

  • It holds a reference (a pointer) to the object { name: 'Suresh' }.

  • Important: const means you cannot reassign emp to point to another object, but you can change the properties of the object.


Step 2: First Output
document.write(emp.name);
  • Prints Suresh.


Step 3: Reassignment Attempt
emp = { name: 'Ravi' };
  • ❌ This line will throw an error:
    Uncaught TypeError: Assignment to constant variable.

  • Because emp is a const — you cannot assign a new object to it.

👉 As soon as this error happens, the script stops execution, so the next lines will not run.


Step 4: Property Update (if reassignment wasn’t there)

If instead of reassigning, you only updated the property like this:

emp.name = 'Ravi';
document.write('<br>'+emp.name);
  • This works ✅ because you’re not changing the reference, only the internal property.

  • Output would be:

Suresh
Ravi

Final Behavior of Your Code
  • It prints:

Suresh
  • Then throws an error at emp = {name:'Ravi'} and stops.


Key Takeaway:

  • const protects the reference (you can’t reassign it).

  • But you can still change the contents (object properties).


👉 Do you want me to rewrite your code correctly so it prints

Suresh
Ravi

without any error?

Best Practice Recommendation:

    • Default to const: Use it by default for every variable. Its immutability makes code easier to reason about.

    • Use let for mutability: If you need to reassign a variable (e.g., a loop counter), use let.

    • Avoid var: There is almost no need to use var in modern ES6+ code. Its function-scoping behavior is more error-prone. Its use should be considered a legacy pattern.

What is this keyword?

this is a special keyword in every function execution context. Its value is a reference to the object that is currently executing the function.  this is not assigned a value until a function is invoked. You cannot look at a function’s definition and know what this is; you must look at how the function is called

What is a closure?

A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created. or simple, a closure gives a function access to variables from an outer function’s scope even after the outer function has returned. It “closes over” or “remembers” its birth environment.

What is Template String?

In JavaScript, template strings (also called template literals) are a special type of string introduced in ES6 (ECMAScript 2015). They make working with strings easier, especially when you want to insert variables, expressions, or create multi-line strings. Template strings are defined using **backticks ()** instead of single () or double (“`) quotes.

Basic Syntax:

let message = `Hello, Welcome to JavaScript World..!`;
console.log(message); // Output: Hello, World!

String Interpolation (Embedding Variable):

You can directly insert variables or expressions into a template string using ${}.

let name = “Suresh”;
let age = 36;

let intro = `My name is ${name} and I am ${age} years old.`;
console.log(intro);
// Output: My name is Suresh and I am 36 years old.

Here, ${name} and ${age} are placeholders that get replaced with their values.

Expressions inside Template Strings:

You can even write expressions (like calculations or function calls) inside ${}.

let a = 10;
let b = 20;

let result = `The sum of ${a} and ${b} is ${a + b}`;
console.log(result);
// Output: The sum of 10 and 20 is 30

Multi-line Strings:

With normal strings, writing multi-line text required \n.  Template strings allow multi-line text directly.

let poem = `Roses are red,
Violets are blue,
JavaScript is fun.`;

console.log(poem);

Output:
Roses are red,
Violets are blue,
JavaScript is fun.

Tagged Templates (Advanced Feature):

Template strings also support tagged templates, where you can use a function to process the string parts.

function highlight(strings, value) {
return `${strings[0]}**${value}**${strings[1]}`;
}

let name = “Suresh”;
let result = highlight`Hello, ${name}! Welcome.`;

console.log(result);
// Output: Hello, **Suresh**! Welcome.

Feature Description Example Output
Basic Usage Defined using backticks instead of quotes js let msg = `Hello JS`; console.log(msg); Hello JS
Variable Interpolation Insert variables inside ${} js let name = "Suresh"; console.log(`Hi, ${name}`); Hi, Suresh
Expression Evaluation Can evaluate expressions inside ${} js let a=5, b=10; console.log(`Sum: ${a+b}`); Sum: 15
Multi-line String Write multi-line strings without \n js let poem = `Line1 \nLine2`; console.log(`Hello,\nWorld!`); Hello, World! (multi-line)
Function Calls in ${} Call functions inside template strings js function greet(){return "Good Day";} console.log(`Message: ${greet()}`); Message: Good Day
Tagged Templates (Advanced) Use a function to process the template js function hi(str, val){return str[0]+val.toUpperCase()+str[1];} let name="suresh"; console.log(hi`Hello, ${name}!`); Hello, SURESH!

What is an arrow function?

Arrow Functions were introduced in ES6 (ECMAScript 2015). They provide a shorter syntax for writing functions and also behave differently with the this keyword compared to regular functions.

Traditional Function:

function add(a, b) {
return a + b;
}

Arrow Function:

const add = (a, b) => a + b;

Syntax of Arrow Functions:

const functionName = (parameters) => expression;

  • If there is only one parameter, parentheses () can be omitted.

  • If the function body has only one statement, curly braces {} and return can be omitted.

With no parameters:

const greet = () => “Hello, World!”;
console.log(greet()); // Hello, World!

With one parameter:

const square = x => x * x;
console.log(square(6)); // 36

With multiple parameters:

const multiply = (a, b) => a * b;
console.log(multiply(4, 5)); // 20

With multiple statements (need {} and return):

const calculate = (a, b) => {
let sum = a + b;
let product = a * b;
return { sum, product };
};
console.log(calculate(3, 4));
// Output: { sum: 7, product: 12 }

this Behavior:
Example with Regular Function:

function Person() {
this.age = 0;
setInterval(function() {
this.age++;
console.log(this.age);
}, 1000);
}
new Person();
// Output: NaN (because “this” refers to global object, not Person)

Example with Arrow Function:

function Person() {
this.age = 0;
setInterval(() => {
this.age++;
console.log(this.age);
}, 1000);
}
new Person();
// Output: 1, 2, 3… (because arrow function uses surrounding “this”)

Arrow functions are best when you want to preserve the surrounding this.

Arrow Functions in JavaScript – Quick Reference
Case Syntax / Example Output
Basic Arrow Function js const add = (a, b) => a + b; console.log(add(3, 4)); 7
Single Parameter (no need for () ) js const square = x => x * x; console.log(square(5)); 25
No Parameters (use () ) js const greet = () => "Hello!"; console.log(greet()); Hello!
Multi-line Body (use {} + return) js const calc = (a, b) => { let sum = a+b; let prod = a*b; return {sum, prod}; }; console.log(calc(2,3)); { sum: 5, prod: 6 }
With Array Methods js let nums=[1,2,3]; let doubled = nums.map(n => n*2); console.log(doubled); [2, 4, 6]
Lexical this (works inside setInterval) js function Person(){ this.age=0; setInterval(()=>{ this.age++; console.log(this.age); },1000);} new Person(); Prints 1,2,3...
Feature Regular Function Arrow Function
Syntax function add(a, b) { return a+b; } const add = (a, b) => a+b;
this binding Has its own this Does not have its own this, it uses the surrounding (lexical) this
Arguments Object Has arguments object Does not have arguments object
Constructor Usage Can be used as a constructor (new) ❌ Cannot be used as constructor
Code Length Longer Shorter & concise

Definition :

The Rest Operator (...) in JavaScript is used to collect multiple elements into a single variable, and is commonly used in functions, arrays, and objects.

  • The Rest Operator is written as ... (three dots).

  • It allows us to collect multiple elements into a single variable (usually an array).

  • Introduced in ES6 (2015).

  • Mainly used in functions and destructuring.

Rest Operator in Functions :

Collects all remaining arguments into a single array.

function showNumbers(...numbers) {
console.log(numbers);
}
showNumbers(1, 2, 3, 4, 5);

Mixing normal and rest parameters :

Rule: Rest operator must be the last parameter in a function.

function intro(name, ...hobbies) {
console.log("Name:", name);
console.log("Hobbies:", hobbies);
}
intro("Suresh", "Reading", "Coding", "Traveling");


Output:
Name: Suresh
Hobbies: [ 'Reading', 'Coding', 'Traveling' ]

Rest Operator in Array Destructuring :

Collects the remaining items of an array into a new array.
let fruits = ["Apple", "Banana", "Mango", "Orange"];
let [first, second, ...restFruits] = fruits;

console.log(first); // Apple
console.log(second); // Banana
console.log(restFruits); // [ 'Mango', 'Orange' ]

Rest Operator in Object Destructuring :

Collects the remaining properties into a new object.

let student = { name: "Suresh", age: 21, grade: "A", city: "Hyderabad" };

let { name, ...restDetails } = student;

console.log(name); // Suresh
console.log(restDetails); // { age: 21, grade: 'A', city: 'Hyderabad' }

Definition :

The Spread Operator (...) in JavaScript is used to expand (spread) the elements of an iterable (array, object, string, etc.) into individual elements.

  • Introduced in ES6 (ECMAScript 2015).

  • Works with arrays, objects, and function calls.

  • Helps in copying, merging, passing arguments, and destructuring.

 It is the opposite of the Rest Operator:

  • Rest → Collects elements into a single variable.

  • Spread → Expands elements into individual values.

Spread in Arrays :

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];

// Merge arrays
let merged = […arr1, …arr2];
console.log(merged); // [1, 2, 3, 4, 5, 6]

// Copy array
let copyArr = […arr1];
console.log(copyArr); // [1, 2, 3]

Spread in Objects :

let person = { name: “Suresh”, age: 25 };
let details = { city: “Hyderabad”, country: “India” };

// Merge objects
let profile = { …person, …details };
console.log(profile);
// { name: “Suresh”, age: 25, city: “Hyderabad”, country: “India” }

// Copy object
let copyPerson = { …person };
console.log(copyPerson);
// { name: “Suresh”, age: 25 }

Spread in Function Calls :

function add(a, b, c) {
return a + b + c;
}

let numbers = [10, 20, 30];

// Spread passes array elements as arguments
console.log(add(…numbers)); // 60

Spread with Strings :

let word = “Hello”;
let letters = […word];

console.log(letters);
// [“H”, “e”, “l”, “l”, “o”]

Feature Rest Operator (...) Spread Operator (...)
Purpose Collects values Expands values
Used in Functions, arrays, objects Arrays, objects, function calls
Example function f(...args) f(...array)
Direction Packs elements Unpacks elements

Definition :

Destructuring in JavaScript is a syntax introduced in ES6 that allows you to extract values from arrays or objects and assign them to variables in a simple and clean way.

  • Works with Arrays and Objects.

  • Helps in assigning multiple values at once.

  • Can use default values.

  • Can be combined with rest operator (...).

Array Destructuring :

// Basic example
let numbers = [10, 20, 30];
let [a, b, c] = numbers;

console.log(a); // 10
console.log(b); // 20
console.log(c); // 30

// Skipping values
let [x, , z] = [1, 2, 3];
console.log(x, z); // 1 3

// Default values
let [p, q, r = 5] = [10, 20];
console.log(r); // 5

Object Destructuring :

let person = { name: “Suresh”, age: 25, city: “Hyderabad” };

// Basic
let { name, age } = person;
console.log(name); // Suresh
console.log(age); // 25

// Rename variables
let { city: place } = person;
console.log(place); // Hyderabad

// Default values
let { country = “India” } = person;
console.log(country); // India

Nested Destructuring :

let employee = {
     id: 101,
    details: {
         name: “Ravi”,
         dept: “IT”
    }
};

let { details: { name, dept } } = employee;
console.log(name); // Ravi
console.log(dept); // IT

Destructuring with Rest Operator :

let [first, …rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [2, 3, 4]

let { id, …info } = { id: 1, name: “Sita”, age: 22 };
console.log(id); // 1
console.log(info); // { name: “Sita”, age: 22 }