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
- The Memory Heap (Memory allocation happens)
- 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.
| Step | Action | Stack Contents (Top to Bottom) |
|---|---|---|
| 1 | printSquare(4) is called | printSquare frame |
| 2 | printSquare calls square(4) | square -> printSquare |
| 3 | square calls multiply(4, 4) | multiply -> square -> printSquare |
| 4 | multiply returns 16 | square -> printSquare |
| 5 | square returns 16 | printSquare |
| 6 | console.log(16) is called * | console.log -> printSquare |
| 7 | console.log returns (undefined) | printSquare |
| 8 | printSquare 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.
| Step | Action | Stack Contents (Top to Bottom) |
|---|---|---|
| 1 | printSquare(4) is called | printSquare frame |
| 2 | printSquare calls square(4) | square -> printSquare |
| 3 | square calls multiply(4, 4) | multiply -> square -> printSquare |
| 4 | multiply returns 16 | square -> printSquare |
| 5 | square returns 16 | printSquare |
| 6 | console.log(16) is called * | console.log -> printSquare |
| 7 | console.log returns (undefined) | printSquare |
| 8 | printSquare returns | <empty> |
Differences between var, let, and const.
What is Hoisting?
Hoisting is a direct consequence of the Creation Phase of an Execution Context.
During this phase, the JavaScript engine:
Scans the code for variable declarations (using
var,let,const) and function declarations.For each declaration, it allocates memory and sets up a binding in the current Variable Environment (the scope).
The key difference between
var,let, andconstlies in how this binding is initialized and when it becomes accessible.
var Hoisting:
Behavior: Variables declared with
varare hoisted and auto-initialized toundefined.Mechanism: During the Creation Phase, the JavaScript engine finds
var myVar;, creates the binding in the variable environment, and immediately sets its value toundefined.
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 avar variable before its declaration line will yield undefined, which can be a major source of bugs.
let and const Hoisting
- Behavior: Variables declared with
letandconstare 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.
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 |
Var Example
Step 1: Understanding the loop
The loop runs from
i = 0toi < 5.So, iterations:
i = 0, 1, 2, 3, 4.- Normally, we’d expect to print
01234.
Step 2: setTimeout behavior
setTimeoutschedules the callback function (inside() => { document.write(i) }) to run later, after100 ms.The loop does not wait; it continues executing immediately and finishes before the timeout triggers.
Step 3: Variable scope with var
varis function-scoped, not block-scoped.So there is only one shared
ivariable for all iterations.By the time the
setTimeoutcallback actually executes, the loop has already finished, andihas become 5.
Step 4: What gets printed
Each of the 5
setTimeoutcallbacks will run after ~100ms.At that time,
i = 5for all of them.So, instead of
01234, you get:55555
let Exaple:
Step 1: Loop execution
The loop runs from
i = 1toi = 5(inclusive).So iterations:
1, 2, 3, 4, 5.
Step 2: Role of let
Unlike
var,letis block-scoped.That means in each iteration of the loop, a new copy of
iis created and preserved separately for that iteration.So, each scheduled
setTimeoutcallback keeps the correct value ofiat the moment it was created.
Step 3: setTimeout scheduling
setTimeout(() => { document.write('<br>'+i); }, 100);
schedules the printing ofiafter 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
ivalue (thanks tolet):
1
2
3
4
5
const Example
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
empis declared as a constant reference.It holds a reference (a pointer) to the object
{ name: 'Suresh' }.Important:
constmeans you cannot reassignempto 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
empis aconst— 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:
constprotects 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
letfor mutability: If you need to reassign a variable (e.g., a loop counter), uselet.Avoid
var: There is almost no need to usevarin modern ES6+ code. Its function-scoping behavior is more error-prone. Its use should be considered a legacy pattern.
this keyword
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
Closures and the Module Pattern
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.
Template Strings
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! |
Arrow Functions
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
{}andreturncan 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... |
Arrow Functions vs Regular Functions
| 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 |
Rest Operator (...)
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 :
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' }
Spread Operator (...)
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”]
Difference between Rest and Spread
| 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 |
Destructuring
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 }
