How Javascript works

satyendra_pandey_b62c8218

Satyendra Pandey

Posted on November 28, 2024

How Javascript works

JavaScript is a high-level, interpreted programming language commonly used for web development to create dynamic and interactive web pages. It runs on the browser (client-side) as well as on servers (server-side, with environments like Node.js). Understanding how JavaScript works involves knowing several concepts about its execution model, environment, and key mechanisms like the call stack, event loop, and asynchronous behavior.

Here’s an overview of how JavaScript works:


1. Execution Contexts and Call Stack

JavaScript code is executed in a single-threaded environment, meaning only one operation can be processed at a time. The JavaScript engine uses a call stack to keep track of function calls.

Call Stack

  • Execution Context: When JavaScript code is executed, an execution context is created. There are two types of contexts:
    • Global Context: The default context when code is not inside any function.
    • Function Context: Created when a function is invoked.
  • Call Stack: A stack that keeps track of function calls. When a function is called, it is added to the top of the stack. Once the function finishes executing, it is popped off the stack.
Example:
function first() {
  console.log('First function');
}

function second() {
  first();
  console.log('Second function');
}

second();
Enter fullscreen mode Exit fullscreen mode

Flow:

  1. The second() function is called, which pushes it to the stack.
  2. first() is called within second(), pushing it to the stack as well.
  3. Once first() completes, it is popped off, and the program returns to second().
  4. When second() completes, it is popped off the stack, and the program ends.

2. Event Loop

JavaScript is non-blocking in nature, meaning it can execute asynchronous tasks without blocking the main thread (UI thread in browsers).

  • Event Loop: The event loop is responsible for handling asynchronous operations. It constantly checks if the call stack is empty and if there are any messages (events) in the message queue that need to be processed.

  • Message Queue: When asynchronous tasks (like network requests, setTimeout, etc.) are completed, their callback functions are placed in the message queue. The event loop checks if the call stack is empty and then pushes the callbacks from the message queue to the call stack.

Example of Asynchronous Execution:
console.log("Start");

setTimeout(() => {
  console.log("Timeout 1");
}, 1000);

setTimeout(() => {
  console.log("Timeout 2");
}, 500);

console.log("End");
Enter fullscreen mode Exit fullscreen mode

Flow:

  1. console.log("Start") is executed.
  2. The first setTimeout is scheduled, but it does not block the execution and goes to the event queue after 1 second.
  3. The second setTimeout is scheduled and goes to the event queue after 500ms.
  4. console.log("End") is executed immediately.
  5. After 500ms, the second timeout is moved to the call stack and executed.
  6. After 1000ms, the first timeout is moved to the call stack and executed.

Output:

Start
End
Timeout 2
Timeout 1
Enter fullscreen mode Exit fullscreen mode

3. Execution Environment

JavaScript runs in different environments, each having specific global objects and capabilities:

  • Browser Environment: Includes the DOM (Document Object Model), which allows interaction with HTML elements, and the window object, which is the global object.
  • Node.js Environment: Provides access to the file system, networking, and other server-side features. It also has its own global object called global.

The global object in the browser is window, and in Node.js, it’s global.


4. Synchronous vs Asynchronous Code

JavaScript can execute both synchronous and asynchronous code.

  • Synchronous Code: This type of code is executed one after another. Each statement is executed only when the previous one finishes. It’s blocking in nature, meaning if one operation takes time (e.g., reading a file), the whole script will wait for that operation to finish.

  • Asynchronous Code: This type of code doesn’t block execution. Instead of waiting for the operation to finish, JavaScript continues executing the rest of the code. When the asynchronous operation finishes (like fetching data), the result is handled via callbacks, promises, or async/await.

Example of Synchronous Code:
console.log("A");
console.log("B");
console.log("C");
Enter fullscreen mode Exit fullscreen mode

Output:

A
B
C
Enter fullscreen mode Exit fullscreen mode
Example of Asynchronous Code:
console.log("A");

setTimeout(() => {
  console.log("B");
}, 1000);

console.log("C");
Enter fullscreen mode Exit fullscreen mode

Output:

A
C
B
Enter fullscreen mode Exit fullscreen mode

Here, setTimeout is asynchronous, so "B" is printed after "C".


5. Event Handling in JavaScript

JavaScript is heavily based on events, especially in web development. For example, when you click a button, the browser triggers an event, and JavaScript can listen and respond to that event.

Event-Driven Programming

In the browser, events such as click, submit, and load can be captured using event listeners.

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("Button clicked!");
});
Enter fullscreen mode Exit fullscreen mode

6. Closures

JavaScript functions can "remember" their outer scope, even when the function is executed outside of that scope. This feature is called a closure.

function outer() {
  let counter = 0;
  return function inner() {
    counter++;
    console.log(counter);
  };
}

const increment = outer();
increment();  // 1
increment();  // 2
Enter fullscreen mode Exit fullscreen mode

Here, the inner function has access to counter even after outer has finished executing.


7. Hoisting

In JavaScript, variable and function declarations are hoisted to the top of their scope during the execution phase, meaning they are available even before they are declared in the code.

  • Function declarations are hoisted completely, so you can call them before their definition.
  • Variables declared with var are hoisted but are only assigned when the code execution reaches that point. Variables declared with let and const are not hoisted in the same way.
console.log(foo()); // works because function is hoisted
function foo() {
  return 'Hello!';
}

console.log(bar); // undefined because `bar` is hoisted but not initialized yet
var bar = 'World';
Enter fullscreen mode Exit fullscreen mode

8. Memory Management and Garbage Collection

JavaScript automatically manages memory. When objects are no longer needed, they are marked for garbage collection, and the memory they occupy is freed.

JavaScript uses an algorithm called mark-and-sweep to detect and clean up unused objects.


Summary of JavaScript Execution Flow:

  1. Global Execution Context: JavaScript starts by executing code in the global context.
  2. Function Calls: When a function is invoked, a new execution context is created and added to the call stack.
  3. Event Loop and Asynchronous Code: Asynchronous operations (e.g., setTimeout, network requests) are handled by the event loop, allowing non-blocking execution.
  4. Execution of Callbacks: Once the synchronous code finishes, the event loop moves the callbacks from the message queue to the call stack for execution.
  5. Memory Management: Unused variables and objects are eventually cleaned up by garbage collection.

In Conclusion:

JavaScript is a versatile language that runs on both the browser (client-side) and server (server-side with Node.js). Its execution model involves synchronous and asynchronous code, a call stack, an event loop, and various features like closures and hoisting. By understanding these core principles, you can better manage how your code executes and handle common pitfalls such as asynchronous code execution and memory management.

💖 💪 🙅 🚩
satyendra_pandey_b62c8218
Satyendra Pandey

Posted on November 28, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related