Sitemap

How Javascript Works

7 min readOct 26, 2026

--

Javascript is a single threaded synchronous programming language. yes you might get confuse that we can perform a async operations then how can it be a scnchronous programming language so answer to this is javascript itself is a scnchronous programming language , (browser and nodejs) enviornment is the reason that helps the javascript to do async operations.

When we write JavaScript code and run it in Chrome, the browser uses the V8 engine to execute it. V8 parses the code and uses an interpreter to execute it, while its JIT compilation mechanisms can compile and optimize frequently executed code into machine code. The interpreter and compilers are components of the V8 JavaScript engine. The browser provides the runtime environment around V8, including APIs such as the DOM, timers, and networking. These browser APIs, along with the event loop and task queues, enable JavaScript to handle asynchronous operations without blocking the execution of other JavaScript code while waiting for certain operations to complete.

so this is what the a complete picture looks like.

Press enter or click to view image in full size

so if this is the code :

console.log("Start");

setTimeout(() => {
console.log("Timer finished");
}, 2000);

console.log("End");

When the JavaScript code starts executing, V8 executes console.log("Start"), so "Start" is printed first.

Next, V8 executes the setTimeout() call. The browser's timer API handles the timer and starts counting down for 2 seconds. V8 does not wait for those 2 seconds; it continues executing the next line, console.log("End"), so "End" is printed immediately afterward.

Once the timer expires, the browser makes the timer callback eligible to run by scheduling it as a task. The event loop coordinates when that task can be processed. When the JavaScript execution context is available, V8 executes the callback, printing "Timer finished".

Therefore, the output is "Start", "End", and then "Timer finished".

Execution context

An Execution Context (EC) is the environment in which JavaScript code is evaluated and executed. It keeps track of the information needed to execute the code, including variables & functions.

There are two important types of execution contexts that we commonly learn about: the Global Execution Context (GEC) and the Function Execution Context (FEC).

  1. Global Execution Context

GEC is automatically created at the moment we run our program. there is only one GEC for a whole script file.

2. Function Execution Context

A FEC is created every time a function is called. When a function is finished a FEC is destroyed.

There are two phases of execution context.

  1. Creation Phase (memory creation)
  2. Execution Phase (code execution)

In creation phase js code is scaned and memory allocation and memory setup is done, like all var variables get memory and are set as undefined.

And in execution phase the code run line by line assigning real values to variable expressions are evaluated and function calls are made.

so lets understand by taking a example

var x = 10;

function greet() {
console.log("Hello");
}

greet();
Press enter or click to view image in full size
  • The GEC is created.
  • During creation, x is initialized to undefined, and the greet function declaration is registered.
  • During execution, x is assigned 10.
  • greet() is invoked, creating an FEC.
  • console.log("Hello") executes and prints Hello.
  • The function returns undefined.
  • The global script finishes executing.

Call Stack

The call stack is a data structure used by the JavaScript engine to keep track of which execution contexts are currently active. When a function is called, its execution context is pushed onto the stack. When the function finishes, its context is popped off.

  • Initially, the call stack is empty. Stack: []
  • The GEC is created and pushed onto the call stack. Stack: [GEC]
  • The GEC executes the global code. x is assigned 10, and the greet function declaration is available. The GEC remains on the stack. Stack: [GEC]
  • greet() is called, so its FEC is pushed onto the stack. Stack: [GEC, FEC]
  • The FEC executes console.log("Hello"). Hello is printed. Once greet() finishes, its FEC is popped off the stack. Stack: [GEC]
  • The global script finishes. The GEC is removed from the call stack, leaving it empty. Stack: []

Stack overflow occurs when the call stack exceeds its available capacity, usually because too many function calls are active at the same time.

for ex:

function greet() {
greet();
}

greet();

Hosting

Hoisting is JavaScript’s behavior of processing variable and function declarations before executing the code in their respective scopes.

Example with var

console.log(a);
var a = 10;

Output:

undefined

As we know, when JavaScript starts executing a script, the Global Execution Context (GEC) is created. During the creation phase, the variable a is registered and initialized with undefined because it is declared using var.

Get Krishpatel’s stories in your inbox

Join Medium for free to get updates from this writer.

After that, during the code execution phase, JavaScript executes console.log(a) before reaching the assignment a = 10. Since a currently holds the value undefined, the output is undefined. Later, JavaScript executes a = 10 and assigns the value 10 to the variable.

Example with let

console.log(a);
let a = 10;

Output:

ReferenceError

Unlike var, a let variable is not initialized with undefined during the creation phase. It remains uninitialized in the Temporal Dead Zone (TDZ) until execution reaches its declaration. Therefore, accessing a before its declaration is evaluated results in a ReferenceError.

The same principle applies to const, which also remains uninitialized in the TDZ until its declaration is evaluated.

Function declaration vs functionn expression

Function declaration → fully hoisted. You can call it before it’s defined:

sayHi(); // ✅ "Hi" — works
function sayHi() {
console.log("Hi");
}

Function expression → follows the variable’s hoisting rules. The variable is hoisted, but the function is not assigned until that line runs:

sayBye(); // ❌ TypeError: sayBye is not a function
var sayBye = function () {
console.log("Bye");
};

Here sayBye is hoisted as undefined (because of var), so calling undefined() throws a TypeError. With let/const it would throw a ReferenceError (TDZ) instead.

Scope

In JavaScript, scope defines where a variable can be accessed in your code. It determines which parts of your program can use a particular variable.

JavaScript has four important scope concepts to understand: global scope, function scope, block scope, and lexical scope.

1. Global Scope

A variable declared outside any function or block has global scope. It can be accessed from different parts of the code, including inside functions.

let appName = "MyApp";
function show() {
console.log(appName);
}
show(); // MyApp
console.log(appName); // MyApp

Here, appName is declared outside the function, so both the function and the global code can access it.

2. Function Scope

A variable declared inside a function is accessible only within that function and its nested scopes.

Variables declared using var, let, or const inside a function are all limited to that function.

function test() {
var secret = 42;
let username = "Sara";
const age = 21;
  console.log(secret);   // 42
console.log(username); // Sara
console.log(age); // 21
}
test();console.log(secret); // ReferenceError

The variables are available inside test(), but they cannot be accessed from outside it.

3. Block Scope

A block is a section of code enclosed in curly braces { }, such as the body of an if statement, a loop, or a standalone block.

Variables declared with let and const are block-scoped, meaning they are accessible only within the block where they are declared.

However, var is not block-scoped. It ignores ordinary block boundaries, although it remains limited to its enclosing function if one exists.

{
let a = 1;
const c = 3;
var b = 2;
}
console.log(b); // 2
console.log(a); // ReferenceError
console.log(c); // ReferenceError

Here, a and c remain inside the block, while b can be accessed outside it because it was declared using var.

Remember: let and const respect block boundaries; var does not.

4. Lexical Scope

The word lexical refers to where code is written in the source code.

Lexical scope means that a function’s access to variables is determined by where that function is defined, not where it is called.

Consider this example:

function outer() {
let name = "Sara";
  function inner() {
console.log(name);
}
inner();
}
outer(); // Sara

The inner() function is defined inside outer(). Because of this nesting, it can access the name variable declared in outer().

This is lexical scoping: the inner function can access variables from its surrounding lexical scope.

Lexical scope is also the foundation of closures, which allow a function to retain access to variables from its surrounding scope even after the outer function has finished executing.

Scope chaining

when we use variable , js looks for it in order : current scope , outer scope , outer scope till global scope

let a = 10;
function outer() {
let b = 20;
function inner() {
let c = 30;
console.log(a, b, c); // 10 20 30
}
inner();
}
outer();

Closures

A closure is a function that remembers and can access variables from its lexical environment, even after the outer function has finished executing.

function outer() {
let name = "Sara";

function inner() {
console.log(name);
}

return inner;
}

const myFunc = outer();
myFunc(); // Sara

--

--