To understand how JavaScript works internally, we must separate two things:
-
JavaScript Engine
-
JavaScript Runtime Environment
1. JavaScript Engine
Definition
A JavaScript engine is responsible for executing JavaScript code.
Example engines:
-
V8 (Chrome, Node.js)
-
SpiderMonkey (Firefox)
-
JavaScriptCore (Safari)
How the Engine Works
Step 1: Parsing
-
JS code is parsed
-
Converted into AST (Abstract Syntax Tree)
Example:
Converted into structured tree format.
Step 2: Compilation
Modern engines use JIT (Just-In-Time) compilation.
-
Code is compiled into machine code
-
Optimized during execution
V8 has:
-
Ignition (Interpreter)
-
TurboFan (Optimizer)
Step 3: Execution
-
Code runs in the Call Stack
-
Variables stored in Memory Heap
Engine Components
-
Call Stack → Executes functions
-
Memory Heap → Stores objects and variables
2. JavaScript Runtime
The engine alone cannot handle:
-
Timers
-
HTTP requests
-
DOM manipulation
The runtime environment provides these.
Browser Runtime Includes
-
Web APIs (setTimeout, fetch, DOM)
-
Event Loop
-
Callback Queue
-
Microtask Queue
Node.js Runtime Includes
-
libuv
-
Event Loop
-
File system APIs
-
OS-level APIs
How Everything Works Together
Example:
Flow:
-
console.log runs in Call Stack
-
setTimeout goes to Web API
-
After delay → callback goes to Task Queue
-
Event Loop moves it to Call Stack
-
Executes callback
Output:
Start
End
Timeout
Execution Model Summary
-
JS Engine parses and compiles code
-
Call Stack executes synchronous code
-
Async tasks handled by runtime (Web APIs / libuv)
-
Event Loop manages execution order
-
Microtasks run before macrotasks
The JavaScript memory model explains how memory is allocated, stored, and managed during code execution.
JavaScript automatically manages memory using garbage collection.
1. Memory Components
JavaScript mainly uses two memory areas:
-
Stack
-
Heap
1. Stack Memory
-
Stores primitive values
-
Stores function execution contexts
-
Works in LIFO (Last In, First Out) order
-
Faster access
Stored in stack:
-
number
-
string
-
boolean
-
null
-
undefined
-
symbol
-
bigint
Example:
Here:
-
aandbare stored separately in stack -
Copy is independent
2. Heap Memory
-
Stores objects and reference types
-
Dynamic memory allocation
-
Slower than stack
Stored in heap:
-
Objects
-
Arrays
-
Functions
Example:
Here:
-
Object stored in heap
-
obj1 and obj2 store reference in stack
-
Both point to same heap memory
3. Execution Context
When a function runs:
-
New execution context is created
-
Stored in Call Stack
-
Variables stored in memory
Example:
greet execution context pushed to stack
-
After execution → popped
4. Garbage Collection
JavaScript automatically frees memory using Garbage Collection.
How It Works
Mark-and-Sweep algorithm:
-
Mark reachable objects
-
Remove unreachable objects
If no references exist → memory is cleaned.
Example:
Old object becomes unreachable → garbage collected.
Memory Leak
Occurs when:
-
Objects remain referenced unnecessarily
-
Garbage collector cannot free memory
Common causes:
-
Unremoved event listeners
-
Global variables
-
Closures holding references
JavaScript uses two main memory areas:
-
Stack Memory
-
Heap Memory
1. Stack Memory
Definition
Stack memory is used to store:
-
Primitive values
-
Function execution contexts
-
Local variables
It follows LIFO (Last In, First Out).
Characteristics
-
Stores values directly
-
Fixed size
-
Very fast access
-
Automatically managed
-
Used for function calls
Example (Primitive Copy)
Here:
-
aandbare stored separately in stack -
Copy is independent
2. Heap Memory
Definition
Heap memory is used to store:
-
Objects
-
Arrays
-
Functions
It stores data dynamically.
Characteristics
-
Stores reference types
-
Large memory area
-
Slower than stack
-
Managed by garbage collector
Example (Reference Copy)
Here:
-
Object stored in heap
-
obj1 and obj2 hold reference in stack
-
Both point to same heap object
Garbage Collection (GC) is the automatic process of freeing memory that is no longer used by the program.
JavaScript has automatic memory management, so developers do not manually allocate or free memory.
Why Garbage Collection Is Needed
When objects are created:
Memory is allocated in the heap.
If memory is never released:
-
Application slows down
-
Memory leaks occur
-
Browser crashes
Garbage collection prevents this.
How Garbage Collection Works
Modern JavaScript engines use the Mark-and-Sweep algorithm.
Mark-and-Sweep Algorithm
Step 1: Mark
-
Start from root references (global variables, current execution context)
-
Mark all reachable objects
Step 2: Sweep
-
Remove all unmarked (unreachable) objects
-
Free their memory
Mark-and-Sweep is the primary garbage collection algorithm used by modern JavaScript engines to free unused memory.
It removes objects that are no longer reachable from the root.
Why It Is Needed
When objects are created in heap memory, they remain there until garbage collected.
The challenge:
How does the engine know which objects are safe to remove?
Solution:
Mark-and-Sweep.
How It Works (Step-by-Step)
Step 1: Identify Roots
Roots include:
-
Global variables
-
Variables in current call stack
-
Active function scopes
These are always considered reachable.
Step 2: Mark Phase
-
Starting from roots
-
Recursively mark all reachable objects
-
Anything referenced is marked as “in use”
Step 3: Sweep Phase
-
Traverse entire heap
-
Remove objects not marked
-
Free their memory
A memory leak occurs when memory that is no longer needed is not released because references to it still exist.
This prevents the garbage collector from freeing memory.
1. Global Variables
Objects attached to global scope are never garbage collected until the page closes.
If data keeps growing → memory leak.
2. Unremoved Event Listeners
If DOM elements are removed but their event listeners remain, memory is retained.
If button is removed without removing listener → memory leak.
Fix:
3. Timers (setInterval / setTimeout)
Timers that are never cleared keep references alive.
If not cleared → keeps running forever.
Fix:
4. Closures Holding References
Closures can accidentally retain large objects.
Even if largeData is unused later, closure may retain it.
5. Detached DOM Elements
When elements are removed from DOM but referenced in JS:
If div variable still exists → not garbage collected.
6. Caches That Grow Forever
Objects used as caches without limits:
If never cleared → memory keeps increasing.
7. Circular References (Less Common Now)
Modern GC handles this, but old systems failed here.
How to Prevent Memory Leaks
-
Avoid unnecessary globals
-
Remove event listeners
-
Clear timers
-
Nullify unused references
-
Limit cache size
-
Use WeakMap / WeakSet when appropriate
Hidden Classes are an internal optimization mechanism used by the V8 engine (Chrome, Node.js) to improve property access performance in JavaScript objects.
JavaScript is dynamically typed, but V8 internally tries to make objects behave like statically structured objects using hidden classes.
Why Hidden Classes Are Needed
JavaScript objects are dynamic:
Properties can be added at runtime.
This makes optimization difficult.
To improve performance, V8:
-
Assigns a hidden class to each object
-
Tracks property structure
-
Optimizes access using inline caching
How Hidden Classes Work
When you create objects with the same property structure:
Both objects:
-
Have same properties
-
Added in same order
-
Share the same hidden class
This allows fast property access.
Inline Caching (IC) is a performance optimization technique used by JavaScript engines (like V8) to speed up repeated property access and method calls.
It works closely with hidden classes.
Why Inline Caching Is Needed
JavaScript is dynamically typed:
At runtime, the engine must determine:
-
Does
userhave propertyname? -
Where is it located in memory?
Doing this repeatedly is expensive.
Inline caching stores previous lookup information to make future access faster.
How It Works
First time property is accessed:
Engine:
-
Looks up property
-
Finds its location
-
Stores the result in cache
Next time:
-
Skips full lookup
-
Uses cached information
-
Much faster
Types of Inline Caching
1. Monomorphic
Property accessed on objects with same hidden class.
If all user objects have same structure → optimized.
Best performance.
2. Polymorphic
Property accessed on few different object shapes.
Still optimized but slightly slower.
3. Megamorphic
Property accessed on many different object shapes.
Optimization fails → slower performance.
V8 (Chrome & Node.js engine) uses multiple internal optimizations to make JavaScript execution fast despite being dynamically typed.
1. Just-In-Time (JIT) Compilation
How It Works
V8 does not just interpret code.
It uses:
-
Ignition → Interpreter
-
TurboFan → Optimizing compiler
Flow:
-
Code is interpreted
-
Frequently executed code (“hot code”) is detected
-
TurboFan compiles it into highly optimized machine code
2. Hidden Classes
V8 assigns hidden classes to objects with similar structure.
If objects share the same property order → same hidden class → faster access.
3. Inline Caching (IC)
Caches property lookup results to avoid repeated dynamic resolution.
Best case: Monomorphic (same object shape).
Worst case: Megamorphic (many shapes).
4. Fast Properties
If object structure remains stable:
-
V8 stores properties in optimized format
-
Access becomes very fast
If properties are added/deleted dynamically:
-
Object becomes “dictionary mode”
-
Slower access
A function is referentially transparent if it always produces the same output for the same input and has no side effects.
This means the function call can be replaced with its returned value without changing the program’s behavior.
Key Conditions
A function is referentially transparent if:
-
Same input → Same output
-
No side effects
-
Does not modify external variables
-
Does not change global state
-
Does not perform I/O
-
Does not mutate arguments
-
Side Effects That Break Transparency
-
Modifying global variables
-
Mutating objects
-
Logging
-
API calls
-
DOM manipulation
-
Random values
-
Date/time usage
A side effect occurs when a function modifies something outside its own scope or interacts with external state beyond returning a value.
Examples of Side Effects
1. Modifying Global Variables
This modifies external state → Side effect.
2. Modifying Function Arguments
Mutates object → Side effect.
3. DOM Manipulation
Changes UI → Side effect.
4. API Calls
Interacts with external system → Side effect.
5. Logging
Writes to console → Side effect.
6. Timers
Asynchronous behavior → Side effect.
7. Using Random or Date
Different output each time → Not pure → Side effect-like behavior.
Function composition is a functional programming technique where multiple functions are combined to produce a new function.
The output of one function becomes the input of the next.
Basic Example
Flow:
10 → add5 → 15 → multiply2 → 30
Why Function Composition Is Important
-
Encourages reusable small functions
-
Improves readability
-
Makes code modular
-
Core concept in functional programming
-
Used heavily in libraries like Redux and RxJS
Both compose and pipe are used for function composition.
The only difference is the direction in which functions are executed.
1. compose
Definition
compose executes functions from right to left.
compose(f, g, h)(x) → f(g(h(x)))
Implementation
Example
Execution:
10 → add5 → 15 → multiply2 → 30
Right to left execution.
2. pipe
Definition
pipe executes functions from left to right.
pipe(f, g, h)(x) → h(g(f(x)))
Implementation
Example
Execution:
10 → add5 → 15 → multiply2 → 30
Left to right execution.
Currying is a functional programming technique where a function with multiple arguments is transformed into a sequence of functions, each taking one argument at a time.
Instead of:
It becomes:
Basic Example
Normal function:
Curried version:
15. Pure functions
Definition
A pure function is a function that:
-
Always returns the same output for the same input
-
Has no side effects
It does not modify external state or depend on it.
Example of Pure Function
No external dependency → Pure.
1. Predictability
Same input → Same output.
Easy to reason about behavior.
No hidden dependencies.
2. Easier Testing
No need to mock:
-
Global variables
-
API calls
-
DOM
-
External state
Example:
Simple unit testing.
3. No Side Effects
Pure functions:
-
Do not mutate data
-
Do not change global state
-
Do not create unexpected bugs
This reduces debugging complexity.
4. Better Maintainability
-
Clear input → output relationship
-
No hidden behavior
-
Easier to refactor
Code becomes cleaner and modular.
Memoization is an optimization technique where the results of expensive function calls are cached, so if the same inputs occur again, the cached result is returned instead of recomputing.
It improves performance by avoiding repeated calculations.
Example Without Memoization
The function runs every time.
Example With Memoization
Now it calculates only once.
Why Memoization Works Well
-
Works best with pure functions
-
Output depends only on input
-
No side effects
RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using Observables to handle asynchronous data streams.
It allows you to work with events, HTTP requests, timers, and user interactions as streams of data.
Why RxJS Is Needed
JavaScript async handling:
-
Callbacks → Callback hell
-
Promises → Single value, limited cancellation
RxJS solves this by:
-
Handling multiple values over time
-
Supporting cancellation
-
Providing powerful operators
Core Concept: Observable
An Observable represents a stream of values over time.
Example:
Output:
Hello
World
An Observable is a stream of data that emits values over time.
It is a core concept in RxJS and reactive programming.
An Observable can emit:
-
Multiple values
-
Over time
-
Asynchronously
Why Observables Are Needed
JavaScript async tools:
-
Callback → Hard to manage
-
Promise → Only one value
-
Observable → Multiple values + cancellation + operators
Basic Example
Output:
1
2
3
Both are used to handle asynchronous operations, but they differ significantly in behavior and capabilities.
1. Promise
Definition
A Promise represents a single asynchronous value that will be available in the future.
It resolves once (or rejects once).
Example
Emits one value
Cannot be cancelled
Eager execution (starts immediately)
2. Observable (RxJS)
Definition
An Observable represents a stream of multiple values over time.
It can emit zero, one, or many values.
Example
Can emit multiple values
Can be cancelled (unsubscribe)
Lazy execution (runs on subscribe)
Backpressure is a situation where a data producer generates data faster than the consumer can process it.
It is a flow-control mechanism to prevent memory overload and performance issues.
Simple Explanation
Producer → Sends data
Consumer → Processes data
If producer is faster than consumer → Data builds up → Memory increases → System slows down.
Backpressure helps control this imbalance.
How Backpressure Is Handled
Common strategies:
-
Buffering → Store data temporarily
-
Dropping → Ignore extra data
-
Throttling → Limit rate
-
Pausing producer → Resume when ready
Web Workers allow JavaScript code to run in a separate background thread, parallel to the main thread.
They are used to perform heavy computations without blocking the UI.
Why Web Workers Are Needed
JavaScript is single-threaded.
Heavy tasks like:
-
Large calculations
-
Image processing
-
Data parsing
-
File compression
Can block the main thread → UI freezes.
Web Workers solve this by moving heavy work to a background thread.
How Web Workers Work
-
Main thread creates a worker
-
Worker runs in separate thread
-
Communication happens using message passing
No shared memory (by default).
These are advanced features used for low-level concurrency and shared memory between threads (like Web Workers).
They enable true parallel computation in JavaScript.
1. SharedArrayBuffer
Definition
SharedArrayBuffer is a special type of memory buffer that can be shared between multiple threads.
Unlike normal ArrayBuffer, it allows multiple workers to access the same memory.
Why It Is Needed
Normally, when using Web Workers:
Data is copied (structured clone).
With SharedArrayBuffer:
-
Memory is shared
-
No copying
-
Faster communication
Atomics
Definition
Atomics is a built-in object that provides safe synchronization methods for shared memory.
It prevents race conditions when multiple threads modify shared data.
Why Atomics Is Needed
If two workers modify same memory:
Race condition can occur.
Atomics ensures operations are atomic (completed fully without interruption).
Example
This safely increments index 0.
Common Atomics Methods
-
Atomics.add()
-
Atomics.sub()
-
Atomics.load()
-
Atomics.store()
-
Atomics.compareExchange()
-
Atomics.wait()
-
Atomics.notify()
structuredClone() is a built-in function used to create a deep copy of a value.
It performs cloning using the structured clone algorithm, which supports many complex data types.
Introduced in modern browsers and Node.js (v17+).
Why It Is Needed
Older deep copy method:
Problems:
-
Fails with functions
-
Fails with Date
-
Fails with Map/Set
-
Fails with undefined
-
Fails with circular references
structuredClone() solves these issues.
A deep copy means creating a completely independent copy of an object, including all nested objects. There are multiple ways to create deep copies manually.
structuredClone() is a built-in modern API that performs deep cloning using the structured clone algorithm.
Object.freeze() is a method used to make an object immutable.
Once an object is frozen:
-
New properties cannot be added
-
Existing properties cannot be removed
-
Existing properties cannot be modified
Syntax
Basic Example
27. Object.seal
Object.seal() is a method that seals an object, preventing new properties from being added and existing properties from being deleted.
However, existing properties can still be modified (if writable).
Syntax
Basic Example
Result:
-
Modification allowed
-
Addition blocked
-
Deletion blocked
1. const
Definition
const prevents reassignment of a variable reference.
It does NOT make the object immutable.
Example
Explanation:
-
Cannot reassign
user -
Can modify properties inside object
2. Object.freeze()
Definition
Object.freeze() makes the object immutable (shallow).
-
Cannot add properties
-
Cannot delete properties
-
Cannot modify properties
Example
Now properties cannot change.
A Proxy is an object that wraps another object (target) and intercepts operations performed on it.
It allows you to customize behavior for:
-
Property access
-
Property assignment
-
Function calls
-
Object construction
-
Deletion
-
And more
Why Proxy Is Needed
Normally:
You cannot intercept or control these operations easily.
Proxy allows you to control and redefine these operations.
Basic Syntax
target → Original object
-
handler → Object containing traps (interceptors)
Example: Intercepting Property Access
Output:
Accessing name
Lingesh
A WeakMap is a collection of key-value pairs where:
-
Keys must be objects
-
Keys are weakly referenced
-
Keys can be garbage collected
It is similar to Map, but with memory management benefits.
Why WeakMap Is Needed
In a normal Map:
Even after obj = null:
-
The object still exists in memory
-
Because Map holds a strong reference
This can cause memory leaks.
WeakMap solves this.
How WeakMap Works
In WeakMap:
Now:
-
Object becomes unreachable
-
Garbage collector removes it
-
Entry automatically disappears
Key Characteristics
-
Keys must be objects
-
Values can be anything
-
No iteration methods
-
No size property
-
Not enumerable
A WeakSet is a collection of unique objects where:
-
Only objects can be stored
-
Objects are weakly referenced
-
Objects can be garbage collected
It is similar to Set, but it holds weak references.
Why WeakSet Is Needed
In a normal Set:
Even after obj = null:
-
Object remains in memory
-
Because Set holds strong reference
This may cause memory leaks.
WeakSet solves this.
How WeakSet Works
Now:
-
Object becomes unreachable
-
Garbage collector removes it
-
WeakSet entry disappears automatically
Key Characteristics
-
Stores only objects
-
Weak references (GC-friendly)
-
No size property
-
Not iterable
-
No forEach, keys, values
Code Splitting
Definition
Code splitting is a technique where a large JavaScript bundle is split into smaller chunks that can be loaded on demand.
Instead of loading the entire app at once, only necessary code is loaded initially.
Why It Is Needed
Without code splitting:
-
Entire JS bundle loads at once
-
Large initial load time
-
Slower performance
With code splitting:
-
Smaller initial bundle
-
Faster first paint
-
Better performance
Example (Dynamic Import)
Here:
-
math.jsis loaded only when button is clicked -
Not included in initial bundle
2. Lazy Loading
Definition
Lazy loading means loading resources only when they are needed.
It can apply to:
-
JavaScript modules
-
Images
-
Routes
-
Components
Example (Image Lazy Loading)
Image loads only when near viewport.
Example (React Lazy Loading)
Component loads only when rendered.
The Critical Rendering Path (CRP) is the sequence of steps the browser follows to convert HTML, CSS, and JavaScript into pixels on the screen.
It determines how quickly a page becomes visible to the user.
Steps in Browser Rendering Pipeline
1. HTML Parsing → DOM Tree
-
Browser receives HTML
-
Parses it
-
Converts it into DOM (Document Object Model)
Example:
Becomes:
DOM → div → "Hello"
2. CSS Parsing → CSSOM Tree
-
Browser parses CSS
-
Builds CSSOM (CSS Object Model)
Example:
Creates CSS rules tree.
3. JavaScript Execution
-
JS can modify DOM and CSSOM
-
JS can block rendering if not optimized
-
Synchronous scripts delay rendering
4. Render Tree Construction
Browser combines:
DOM + CSSOM → Render Tree
Render tree contains:
-
Visible elements only
-
Computed styles
Elements like display: none are excluded.
5. Layout (Reflow)
-
Browser calculates size and position of each element
-
Determines geometry
This step is called Layout or Reflow.
6. Paint
-
Browser paints pixels on screen
-
Applies colors, shadows, borders, text
7. Composite
-
Layers are combined
-
Final image rendered on screen
Visual Flow
HTML → DOM
CSS → CSSOM
DOM + CSSOM → Render Tree
Render Tree → Layout
Layout → Paint
Paint → Composite
Reflow
Definition
Reflow occurs when the browser recalculates the layout of elements.
It determines:
-
Size
-
Position
-
Geometry of elements
It happens when something affects page structure.
When Reflow Happens
-
Adding/removing DOM elements
-
Changing element size (width, height)
-
Changing font size
-
Changing position
-
Resizing window
-
Reading layout properties (offsetWidth, etc.)
Example
This changes layout → Reflow triggered.
2. Repaint
Definition
Repaint occurs when an element’s appearance changes but layout remains the same.
Only visual updates are applied.
When Repaint Happens
-
Changing color
-
Changing background
-
Changing visibility
-
Changing outline
Example
No layout change → Only repaint.
Key Difference
-
Reflow → Layout recalculation
-
Repaint → Visual update only
-
Reflow is more expensive
-
Reflow often triggers repaint
Design patterns are reusable solutions to common software design problems.
In JavaScript, patterns are influenced by:
-
Functions as first-class citizens
-
Prototypes
-
Closures
-
Modules
1. Module Pattern
Purpose
Encapsulate private data and expose public methods.
Example
Uses closure for private variables.
2. Factory Pattern
Purpose
Create objects without exposing the creation logic.
Example
3. Constructor Pattern
Purpose
Create multiple instances using a constructor function.
Example
4. Singleton Pattern
Purpose
Ensure only one instance exists.
Example
36. Dependency injection
Dependency Injection is a design pattern where a class or function receives its dependencies from the outside instead of creating them internally.
In simple terms:
Do not create dependencies inside a class — inject them from outside.
Why DI Is Needed
Without DI → Tight coupling
With DI → Loose coupling
Loose coupling improves:
-
Testability
-
Maintainability
-
Flexibility
Without Dependency Injection
Problem:
-
Cannot easily replace Database
-
Hard to unit test
With Dependency Injection
37. Global execution context
The Global Execution Context is the default execution context created when a JavaScript program starts running.
It is the base execution environment where:
-
Global variables are created
-
Functions are defined
-
The
thiskeyword is initialized
It is created automatically by the JavaScript engine.
What Is Execution Context?
An execution context is the environment in which JavaScript code is evaluated and executed.
There are three types:
-
Global Execution Context
-
Function Execution Context
-
Eval Execution Context
What Happens in Global Execution Context?
When JS file runs:
Engine performs two phases:
1. Creation Phase
During this phase:
-
Global object is created
-
Browser → window
-
Node.js → global
-
-
thisis set to global object -
Memory is allocated for variables and functions
Behavior:
-
varvariables → initialized with undefined -
letandconst→ created but not initialized (Temporal Dead Zone) -
Functions → fully stored in memory
2. Execution Phase
-
Code runs line by line
-
Variables assigned actual values
-
Functions executed when called
A Function Execution Context is created every time a function is invoked.
It contains everything needed to run that function:
-
Local variables
-
Function parameters
-
argumentsobject -
thisbinding
It is pushed onto the Call Stack when the function is called.
When Is It Created?
Example:
When greet("Lingesh") runs:
-
A new Function Execution Context is created.
Phases of Function Execution Context
Like the Global Execution Context, it has two phases:
1. Creation Phase
During this phase:
-
Memory is allocated for parameters
-
Local variables are initialized
-
argumentsobject is created -
thisis determined -
Inner functions are stored
Behavior:
-
var→ initialized as undefined -
let&const→ Temporal Dead Zone -
Functions → fully stored in memory
2. Execution Phase
-
Code runs line by line
-
Variables get assigned actual values
-
Return statement executes
-
Context is removed after completion
Every execution context (Global or Function) goes through two main phases:
-
Creation Phase
-
Execution Phase
1. Creation Phase (Memory Phase)
This happens before the code actually runs.
The JavaScript engine prepares memory.
What Happens in Creation Phase?
1. Global Object Creation
-
Browser →
window -
Node.js →
global
2. this Binding
-
In Global Context →
thisrefers to global object -
In Function Context → depends on how function is called
3. Variable Hoisting
During creation phase:
-
ais created in memory -
Initialized as
undefined
4. let and const
They are hoisted but:
-
Not initialized
-
Stay in Temporal Dead Zone (TDZ)
5. Function Hoisting
Functions are fully stored in memory during creation phase.
They can be called before declaration.
2. Execution Phase
After memory setup:
-
Code runs line by line
-
Variables get assigned real values
-
Functions execute
-
Return statements processed
globalThis is a standard way to access the global object across all JavaScript environments.
It provides a unified reference to the global object, regardless of whether the code runs in:
-
Browser
-
Node.js
-
Web Worker
Why It Was Introduced
-
Cross-platform consistency
-
Avoid environment checks
-
Standardized in ES2020
A race condition occurs when multiple asynchronous operations access and modify shared data, and the final result depends on the order in which they complete.
It leads to unpredictable and inconsistent behavior.
Why Race Conditions Happen
JavaScript is single-threaded, but:
-
Async operations (Promises, setTimeout, fetch)
-
Web Workers
-
Shared memory (SharedArrayBuffer)
Can execute in non-deterministic order.
Example of Race Condition
Expected balance: 20
Actual result may be incorrect depending on timing.
Because both functions read the same initial value before update.
How to Prevent Race Conditions
Handling errors in asynchronous code depends on the async pattern used:
-
Callbacks
-
Promises
-
async/await
1. Error Handling in Callbacks (Old Way)
Callbacks usually follow error-first pattern.
Problem:
-
Callback hell
-
Hard to manage
2. Error Handling with Promises
Use .catch().
Errors handled in .catch().