Skip to main content

Command Palette

Search for a command to run...

Javascript Interview - Intermediate

Updated
View as Markdown
  1. What is de-structuring in JavaScript?
    Destructuring in JavaScript is a syntax feature that allows you to unpack values from arrays or properties from objects into distinct variables. It makes extracting data much more concise and readable.
    i) Array Destructuring

     // You can extract values from an array and assign them to variables
     const numbers = [1, 2, 3];
     const [a, b, c] = numbers;
    
     console.log(a); // 1
     console.log(b); // 2
     console.log(c); // 3
    
     // You can also skip elements
     const [first, , third] = numbers;
     console.log(third); // 3
    

    ii) Object Destructuring

     // You can extract values from an object by matching property names
     const person = { name: 'Alice', age: 25 };
     const { name, age } = person;
    
     console.log(name); // Alice
     console.log(age);  // 25
    
     // You can also rename variables
     const { name: fullName } = person;
     console.log(fullName); // Alice
    
  2. How does object destructuring differ from array destructuring in JavaScript?
    i) Object Destructuring
    Based on - Property names
    Order matters? - ❌ No, order doesn’t matter
    Syntax - { key } = object
    Default values - Yes
    Renaming - Yes ({ key: newVar })**
    ii) Array Destructuring**
    Based on - Item positions
    Order matters? - ✅ Yes, order matters
    Syntax - [value] = array
    Default values - Yes
    Renaming - Not directly (use assignment after)

     // Object Destructuring
     const user = { name: "Jane", age: 28 };
    
     // Extracting by property name
     const { name, age } = user;
     console.log(name); // Jane
     console.log(age);  // 28
    
     // Renaming
     const { name: userName } = user;
     console.log(userName); // Jane
    
     // Array Destructuring
     const colors = ["red", "green", "blue"];
    
     // Extracting by position
     const [firstColor, secondColor] = colors;
     console.log(firstColor);  // red
     console.log(secondColor); // green
    
     // Skipping elements
     const [, , thirdColor] = colors;
     console.log(thirdColor); // blue
    

    Use object destructuring when dealing with objects with named properties, like API responses, config objects, etc.
    Use array destructuring when working with ordered collections, like lists, tuples, coordinates, etc.

  3. What are dynamic imports in JavaScript?

    Dynamic imports in JavaScript let you load modules on the fly—only when they are needed—rather than at the top of your file like traditional static imports. They’re useful for code-splitting, lazy loading, and improving performance.

     async function loadModule() {
       try {
         const module = await import('./myModule.js');
         module.default(); // assuming it's a default export
       } catch (err) {
         console.error('Error loading module:', err);
       }
     }
    
  4. What is the use of nullish coalescing operator (??) in Javascript?

    The nullish coalescing operator (??) in JavaScript is used to provide a default value when the left-hand side is either null or undefined.

     let userInput = null;
     let name = userInput ?? "Guest";
    
     console.log(name); // "Guest"
    

    If userInput were "" (an empty string), 0, or false, nullish coalescing operator would not replace it.

  5. What is the use of optional chaining operator (?.) in Javascript?
    The optional chaining operator (?.) in JavaScript is used to safely access deeply nested object properties without having to check if each intermediate property exists.
    If any part before the ?. is null or undefined, the entire expression returns undefined instead of throwing an error.
    Why we use it - To avoid "Cannot read property of undefined" errors when accessing nested values.

     const user = {
       name: "John",
       address: {
         city: "New York"
       }
     };
    
     console.log(user.address?.city);     // "New York"
     console.log(user.profile?.email);    // undefined (no error!)
    
  6. What is the event loop in JavaScript, and how does it work?
    The event loop is the heart of JavaScript's concurrency model. It’s what allows JavaScript (a single-threaded language) to handle asynchronous operations like timers, network requests, and UI events without blocking the main thread.
    How the Event Loop Works -
    Call Stack: Executes functions one at a time (LIFO - last in, first out).
    Web APIs (Browser or Node.js APIs): Handle things like setTimeout, fetch, or DOM events.
    Callback / Task Queue (or Message Queue): Stores async callbacks until the stack is free.
    Event Loop: Monitors the call stack and task queue. When the stack is empty, it pushes the next task from the queue into the stack.

  7. What is the difference between shallow copy and deep copy in JavaScript, Explain with example?

    i) Shallow Copy - Copies only one level of the object. Nested objects still reference the original.
    ii) Deep Copy - Recursively copies all levels, creating completely independent clones.

     // Shallow Copy
     const original = {
       name: "Alice",
       address: { city: "Wonderland" }
     };
    
     // Create shallow copy
     const shallowCopy = { ...original };
    
     // Modify nested property
     shallowCopy.address.city = "New York";
    
     console.log(original.address.city);  // "New York" 😱 (original changed!)
    

    Even though we used the spread operator { ...original }, it only made a shallow copy—address is still shared.

     // Deep Copy
     const original = {
       name: "Alice",
       address: { city: "Wonderland" }
     };
    
     // Create deep copy using JSON
     const deepCopy = JSON.parse(JSON.stringify(original));
    
     // Modify nested property
     deepCopy.address.city = "New York";
    
     console.log(original.address.city);  // "Wonderland" ✅ (original untouched)
    

    Now the two objects are completely separate.
    Shallow copy is fine for simple or flat structures. Deep copy is safer for nested structures, especially when immutability matters (e.g., in state management like React).
    Common Ways to Copy -

    i) Object.assign({}, obj)
    Type - Shallow
    Notes - Only top-level copy
    ii) { ...obj }
    Type - Shallow
    Notes - Spread syntax, shallow only

    iii) JSON.parse(JSON.stringify(obj))
    Type - Deep
    Notes - Doesn’t handle functions, undefined, or circular refs
    iv) structuredClone(obj)
    Type - Deep
    Notes - Handles most cases, including Date, Map, Set, etc (modern browsers)

  8. What is the difference between call(), apply(), and bind() in JavaScript, Explain with example?
    call(), apply(), and bind() are Function methods in JavaScript that let you control the this context when invoking or preparing to invoke a function. They're super useful when borrowing functions or working with dynamic context.

     const person = {
       fullName: function(city, country) {
         return `${this.firstName} ${this.lastName} from ${city}, ${country}`;
       }
     };
    
     const user = {
       firstName: "Alice",
       lastName: "Johnson"
     };
    
     const result = person.fullName.call(user, "New York", "USA");
     console.log(result); // "Alice Johnson from New York, USA"
    
     const result = person.fullName.apply(user, ["Paris", "France"]);
     console.log(result); // "Alice Johnson from Paris, France"
    
     const boundFunc = person.fullName.bind(user, "London", "UK");
     console.log(boundFunc()); // "Alice Johnson from London, UK"
    

    Key Differences -
    i) call()
    Invokes function? - Immediately
    Passes arguments - Individually (arg1, arg2)
    Changes this? - YES
    ii) apply()
    Invokes function? - Immediately
    Passes arguments - As an array ([arg1, arg2])
    Changes this? - YES
    iii) bind()
    Invokes function? - Later (returns new function)
    Passes arguments - Individually (arg1, arg2)
    Changes this? - YES

  9. What is the difference between map(), filter(), and reduce() in Javascript, Explain with example?
    map(), filter(), and reduce() are array methods in JavaScript that help you transform, select, or combine data in arrays in a clean, functional way.
    i) map() - Transform elements
    Purpose - Transforms each element
    Returns - New array
    Input - All items
    ii) filter() - Select elements that pass a condition
    Purpose - Filters elements based on condition
    Returns - New array
    Input - Some items

    iii) reduce() - Combine all elements into a single result
    Purpose - Reduces array to a single value
    Returns - Anything (value)
    Input - All items

     const numbers = [1, 2, 3, 4, 5];
    
     const doubled = numbers.map(num => num * 2);
     console.log(doubled); // [2, 4, 6, 8, 10]
    
     const evens = numbers.filter(num => num % 2 === 0);
     console.log(evens); // [2, 4]
    
     const sum = numbers.reduce((acc, curr) => acc + curr, 0);
     console.log(sum); // 15
    
  10. What is the difference between some() and every() in Javascript, Explain with example?

    i) some()
    What it checks - At least one element passes
    Returns - true/false
    Stops when - It finds the first match
    Use this when you want to - Check if any item satisfies a condition
    ii) every()

    What it checks - All elements must pass

    Returns - true/false

    Stops when - It finds the first failure
    Use this when you want to - Check if all items satisfy a condition

    const users = [
      { name: "Alice", isOnline: true },
      { name: "Bob", isOnline: false },
      { name: "Charlie", isOnline: true }
    ];
    
    // ✅ Check if *some* users are online
    const someoneOnline = users.some(user => user.isOnline);
    console.log(someoneOnline); // true
    
    // ✅ Check if *all* users are online
    const everyoneOnline = users.every(user => user.isOnline);
    console.log(everyoneOnline); // false
    
  11. Explain the concept of prototypes and prototype chain in JavaScript.

    In JavaScript, prototypes and the prototype chain are core concepts that allow objects to inherit properties and methods from other objects. This is what makes JavaScript's object-oriented system unique and dynamic.
    Every JavaScript object has a prototype—a reference to another object. This prototype object can have properties and methods that are inherited by the original object.
    Prototype is essentially a blueprint for creating objects. If an object does not have a property or method that you're trying to access, JavaScript will check its prototype for that property or method.
    Prototype Chain -
    i) The prototype chain is the mechanism that allows JavaScript objects to inherit from other objects. It’s a chain of objects linked through their prototypes.
    ii) Every object has a hidden internal property called [[Prototype]], which points to another object (its prototype).
    iii) If the property or method isn’t found on the current object, JavaScript looks up the chain of prototypes to find it.
    iv) The chain ends at null, which is the prototype of the Object prototype.

    // Creating a constructor function for a person
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    
    // Adding a method to the prototype of Person
    Person.prototype.sayHello = function() {
      console.log(`Hello, my name is ${this.name}`);
    };
    
    // Creating a new object from Person
    const alice = new Person("Alice", 30);
    
    // Calling the inherited method
    alice.sayHello(); // "Hello, my name is Alice"
    
    // Inspecting the prototype chain
    console.log(alice.__proto__ === Person.prototype); // true
    console.log(Person.prototype.__proto__ === Object.prototype); // true
    console.log(Object.prototype.__proto__); // null
    
  12. What is the purpose of Object.freeze() and Object.seal()?

    Object.freeze() and Object.seal() are both methods in JavaScript that control how objects can be changed—but they work at different levels of restriction.
    i) Object.freeze()
    Purpose - Make an object completely immutable.
    What it does - Prevents adding, removing, or changing any properties. Makes existing properties non-writable and non-configurable.
    Use freeze() when you want to completely lock an object so no one can change anything about it.

    const user = {
      name: "Alice",
      age: 25
    };
    
    Object.freeze(user);
    
    user.age = 30;       // ❌ Fails silently (in strict mode: TypeError)
    user.city = "Paris"; // ❌ Fails silently
    delete user.name;    // ❌ Fails silently
    
    console.log(user); // { name: "Alice", age: 25 }
    

    ii) Object.seal()
    Purpose - Make an object non-extensible but still editable
    What it does - Prevents adding or deleting properties. Allows changing existing property values. Properties become non-configurable (you can't reconfigure or delete them), but still writable.
    Use seal() when you want to lock down the shape of an object but still allow value updates.

    const settings = {
      theme: "dark",
      fontSize: 14
    };
    
    Object.seal(settings);
    
    settings.theme = "light";    // ✅ Allowed
    settings.newProp = true;     // ❌ Not allowed
    delete settings.fontSize;    // ❌ Not allowed
    
    console.log(settings); // { theme: "light", fontSize: 14 }
    
  13. What are WeakMap and WeakSet in JavaScript?
    WeakMap and WeakSet are special versions of Map and Set in JavaScript, with one big difference: they allow "weak references" to objects, which means they don't prevent garbage collection.
    In WeakMap and WeakSet, the references to the keys (or values) do not stop them from being garbage collected if there are no other references to those objects.

    i) WeakMap
    Stores key-value pairs.
    Keys must be objects (not primitives).
    Keys are held weakly (garbage collectible).
    Not iterable (can’t loop over it).
    Methods: .set(), .get(), .has(), .delete()

    const weakMap = new WeakMap();
    
    let obj = { name: "Alice" };
    weakMap.set(obj, "some metadata");
    
    console.log(weakMap.get(obj)); // "some metadata"
    
    obj = null; // Now the key object is eligible for garbage collection
    // weakMap will automatically remove the entry when GC happens
    

    ii) WeakSet
    Stores objects only (no primitives).

    Values are held weakly.

    Not iterable (no for...of, .size, etc.).

    Methods: .add(), .has(), .delete()

    const weakSet = new WeakSet();
    
    let user = { name: "Bob" };
    weakSet.add(user);
    
    console.log(weakSet.has(user)); // true
    
    user = null; // Now object can be garbage collected
    // weakSet will automatically remove the object
    
  14. What is the difference between localStorage and sessionStorage?
    Both localStorage and sessionStorage are part of the Web Storage API, used for storing data in the browser. But they differ in scope, lifetime, and usage.
    i) localStorage
    Lifetime - Persistent (stays after page reload or browser close)
    Scope - Shared across all tabs/windows of the same origin
    Storage limit - ~5–10MB
    API syntax - Same (setItem(), getItem(), etc.)
    Auto-expiry - ❌ No
    ii) sessionStorage
    Lifetime - Temporary (cleared when tab is closed)
    Scope - Unique to a single tab/session
    Storage limit - ~5–10MB
    API syntax - Same
    Auto-expiry - ✅ Yes (on tab close)

    // Save to localStorage
    localStorage.setItem("username", "Alice");
    
    // Get from localStorage
    const user = localStorage.getItem("username");
    console.log(user); // "Alice"
    
    // Data stays even after page refresh or browser close
    
    // Save to sessionStorage
    sessionStorage.setItem("cart", JSON.stringify(["apple", "banana"]));
    
    // Get from sessionStorage
    const cart = JSON.parse(sessionStorage.getItem("cart"));
    console.log(cart); // ["apple", "banana"]
    
    // Data disappears when tab is closed
    

    Use Case Summary -
    Remember login status or theme preferences - localStorage
    Store temporary form data while filling - sessionStorage
    Maintain state only during a specific tab session - sessionStorage

  15. How do localStorage and cookies differ?

    localStorage and cookies are both used for storing data in the browser, but they have different purposes, storage limits, behavior, and accessibility.
    i) localStorage
    Storage size - ~5–10 MB
    Expires - Stays until manually cleared
    Accessed by - JavaScript only
    Scope - Domain + protocol
    Use cases - Storing large data client-side (e.g. theme, token)
    Performance - Fast, not sent with requests
    Security - No automatic security layer
    ii) cookies
    Storage size - ~4 KB (very limited)
    Expires - Can be set to expire at a specific time
    Accessed by - JavaScript and sent with every HTTP request
    Scope - Domain + path + protocol
    Use cases - Server communication, session management
    Performance - Slower, included in every HTTP request
    Security -Can be HttpOnly and Secure

    // Store data
    localStorage.setItem("theme", "dark");
    
    // Retrieve data
    const theme = localStorage.getItem("theme");
    

    Stored only in the browser.

    Not sent to the server automatically.

    // JavaScript way (client-side)
    document.cookie = "user=Alice; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/";
    

    Stored in the browser and sent to the server on each HTTP request

    Useful for authentication, session IDs, and server-side reading

    Use Case Summary -
    Store a user’s theme, preferences, or local data - localStorage
    Track logged-in sessions across pages and with the server - Cookies
    Store large JSON objects on the client - localStorage
    Make data available to both browser and server - Cookies

  16. What is the DOM in JavaScript?
    The DOM (Document Object Model) is a programming interface that represents the structure of a web page in a way that JavaScript can interact with it.
    The DOM is a tree-like structure where each element, attribute, and piece of text in an HTML document is represented as a node.
    It allows JavaScript to read, modify, and react to HTML content and structure dynamically.
    When the browser loads a webpage -

    It parses the HTML.

    It builds a DOM tree that represents all the elements.

    JavaScript can then access and manipulate this tree.

    <!DOCTYPE html>
    <html>
      <body>
        <h1 id="title">Hello World</h1>
        <p class="message">Welcome!</p>
      </body>
    </html>
    
    // Access element by ID
    const heading = document.getElementById("title");
    heading.textContent = "Hello JavaScript!";
    
    // Access element by class
    const message = document.querySelector(".message");
    message.style.color = "blue";
    

    Common DOM Methods -
    getElementById(id) - Finds an element by ID
    getElementsByClassName(class) - Finds elements by class (HTMLCollection)
    querySelector(selector) - Finds the first element matching CSS selector
    querySelectorAll(selector) - Finds all elements matching CSS selector
    createElement(tag) - Creates a new element node

    // getElementById(id)
    const title = document.getElementById("main-title");
    // getElementsByClassName(class)
    const items = document.getElementsByClassName("menu-item");
    // querySelector(selector)
    const divs = document.getElementsByTagName("div");
    // querySelectorAll(selector)
    const heading = document.querySelector("h1");
    const input = document.querySelector("#search-box");
    const button = document.querySelector(".btn.primary");
    // querySelectorAll(selector)
    const buttons = document.querySelectorAll(".btn");
    buttons.forEach(btn => btn.style.color = "red");
    

    Why is the DOM important? -
    Update content dynamically

    Change styles

    Handle events like clicks or keyboard input

    Create, move, or delete elements

    document.querySelector("#submitBtn").addEventListener("click", () => {
      const input = document.querySelector("#nameInput").value;
      document.querySelector("#greeting").textContent = `Hello, ${input}!`;
    });
    
  17. What is the BOM (Browser Object Model)?
    The BOM (Browser Object Model) refers to everything JavaScript can interact with outside the webpage content itself — it's how JavaScript talks to the browser.
    The BOM is a collection of objects provided by the browser that allows JavaScript to:

    Interact with the browser window

    Control the URL and navigation

    Work with popups, alerts, and timers

    Access browser history, screen, and more
    BOM vs DOM
    Purpose - DOM interacts with webpage content, BOM interacts with browser features
    Example - DOM document.getElementById(), BOM window.alert(), location.href
    Defined by - DOM W3C/WHATWG, BOM Browser vendors (not standardized)

  18. Explain the fetch API?
    The Fetch API is a modern way in JavaScript to make HTTP requests (like GET, POST, etc.) to servers — and it replaces the older XMLHttpRequest method with a cleaner, promise-based approach.
    Fetch API - Built into modern browsers. Returns a Promise. Used to fetch data from a server (like an API). Works with async/await or .then() chaining.

    // GET request
    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then(res => res.json()) // parse JSON from the response
      .then(data => console.log(data))
      .catch(err => console.error("Fetch error:", err));
    
    // POST request
    fetch("https://jsonplaceholder.typicode.com/posts", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        title: "Hello",
        body: "This is a new post",
        userId: 1
      })
    })
      .then(res => res.json())
      .then(data => console.log(data));
    

    fetch doesn't reject on HTTP errors like 404 or 500 — you need to check response.ok manually.

  19. Explain the AXIOS?
    Axios is a popular JavaScript library for making HTTP requests — just like the fetch API, but with more features out of the box.
    What is Axios -
    A promise-based HTTP client for the browser and Node.js.

    Built on top of XMLHttpRequest.

    Supports:

    i) Request and response interceptors

    ii) Automatic JSON transformation

    iii) Timeouts, cancellation, and more

    iv) Works seamlessly with async/await

    // using Promise
    axios.get(url)
      .then(response => console.log(response.data))
      .catch(error => console.error(error));
    
    // using Async/Await
    const response = await axios.get(url);
    console.log(response.data);
    
    axios.get("https://jsonplaceholder.typicode.com/posts/1")
      .then(res => {
        console.log(res.data);
      });
    
    axios.post("https://jsonplaceholder.typicode.com/posts", {
      title: "Hello",
      body: "This is a new post",
      userId: 1
    })
      .then(res => console.log(res.data))
      .catch(err => console.error(err));
    

    Installing Axios -

     // If you're using Node.js or bundlers like Webpack
    npm install axios
    
    // via CDN for front-end
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    
  20. What is difference between fetch and Axios?

    i) Axios automatically transforms JSON, fetch requires res.json()
    ii) Axios does better error handling via HTTP errors throw but in fetch API you need manual check for .ok
    iii) Axios has request & response interceptors but fetch API does not have it.

    iv) Axios can works in Node.js but fetch API can work only in browsers.

  21. What is JWT (JSON Web Token) in Javascript, and how is it used for securing APIs?
    JWT (JSON Web Token) is a compact, URL-safe way to represent claims (like user identity or permissions) that can be verified and trusted. It's commonly used to secure APIs and manage authentication in modern web applications.
    A JWT is a string with three parts, separated by dots (.)
    header.payload.signature
    Parts of a JWT -
    i) Header: Metadata about the token

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    

    ii) Payload: Contains the actual data (user info, roles, etc.)

    {
      "userId": 123,
      "email": "joe@gmail.com"
    }
    

    iii) Signature: Ensures the token hasn’t been tampered with (generated using a secret key)
    How JWT Works for Securing APIs -
    i) Login: User logs in with credentials.

    ii) Server validates credentials and generates a JWT (with secret key).

    iii) Client stores JWT (usually in localStorage or sessionStorage).

    iv) For future API calls, the client sends the JWT in the Authorization header:

    Authorization: Bearer <token>
    

    JWT Usage Example in JavaScript -

    // Backend (Node.js using jsonwebtoken)
    const jwt = require("jsonwebtoken");
    
    const token = jwt.sign({ userId: 123 }, "your-secret-key", { expiresIn: "1h" });
    console.log(token);
    
    // Frontend (sending token with Axios)
    axios.get("/api/protected", {
      headers: {
        Authorization: `Bearer ${yourToken}`
      }
    });
    

    Benefits of Using JWT -

    i) Stateless: No need for server-side sessions.

    ii) Secure: Signature verifies data integrity.

    iii) Scalable: Ideal for microservices and APIs.

    iv) Portable: Can be passed in HTTP headers, cookies, etc.
    JWT Security Tips -
    i) Always use HTTPS.

    ii) Store tokens safely (e.g., not in localStorage for highly sensitive data).

    iii) Use short expiration times and refresh tokens.

    iv) Validate JWTs on every request to protected resources.

  22. How does token-based authentication works in Javascript, and how is it different from session-based authentication?
    Token-Based Authentication -
    i) Login: User sends credentials (e.g., username/password) to the server.

    ii) Server validates the credentials and generates a JWT (JSON Web Token).

    iii) The token is sent back to the client (browser or mobile app).

    iv) The client stores the token (usually in localStorage or sessionStorage).

    v) For each subsequent request, the token is sent in the Authorization header

    Authorization: Bearer <token>
    

    vi) The server verifies the token on each request (no session stored).
    vii) If the token is valid, access is granted.

    Pros -

    i) Stateless: No session stored on the server.

    ii) Scalable: Ideal for distributed systems, APIs, and mobile apps.

    iii) Cross-platform: Easy to use on web, mobile, etc.
    Cons -
    i) Tokens can be stolen if stored insecurely (like in localStorage).

    ii) Harder to revoke access instantly (e.g., logout everywhere).
    Session-Based Authentication -
    i) Login: User sends credentials to the server.

    ii) Server validates the credentials and creates a session (usually with a unique session ID).

    iii) The session ID is stored in a cookie and sent to the client.

    iv) For each request, the cookie is sent automatically to the server.

    v) The server matches the session ID to stored session data (like user info).

    vi) If valid, the user is authenticated.
    Props -
    i) Easier to invalidate or revoke sessions.

    ii) Safer in trusted environments (especially with HTTP-only cookies).
    Cons -
    i) Server must store session data (not stateless).

    ii) Harder to scale across multiple servers without shared session storage.

    iii) CSRF attacks can be a concern if not protected properly.

    // Token-Based
    // Storing token
    localStorage.setItem("token", receivedToken);
    
    // Sending token with request
    fetch("/api/user", {
      headers: {
        Authorization: `Bearer ${localStorage.getItem("token")}`
      }
    });
    
    // Session Based - 
    // Server sets cookie with session ID
    Set-Cookie: sessionId=abc123; HttpOnly
    
    // Client automatically sends cookie on each request
    
  23. What is OAuth in Javascript, and how does it secure API access?

    OAuth (Open Authorization) is a protocol that allows a third-party application (like your JavaScript app) to access user data from another service (like Google, GitHub, Facebook) — without sharing the user's credentials.
    OAuth is for authorization, not authentication (though it's often used for "Login with X").

    It allows safe, delegated access to APIs using access tokens, not user passwords.

    It's a standard way to secure third-party API access in modern apps.
    Common Terms -
    i) Resource Owner - The user who owns the data
    ii) Client - The app requesting access (e.g. your frontend app)
    iii) Authorization Server - The server that authenticates the user (e.g. Google login)
    iv) Resource Server - The API you want to access (e.g. GitHub API)
    v) Access Token - A token used to access protected resources

    How OAuth Works (Simplified Flow) -
    i) User clicks “Login with Google” on your site.

    ii) Your app redirects the user to Google’s auth server with a request.

    iii) The user logs in and consents to share data.

    iv) Google redirects back to your app with an authorization code.

    v) Your app exchanges that code for an access token (and optionally a refresh token).

    vi) Your app uses the access token to call Google APIs on behalf of the user.
    How OAuth Secures API Access -

    i) No passwords are shared with your app.

    ii) Access is granted using tokens, not credentials.

    iii) Tokens can be scoped (e.g., only read email, not modify contacts).

    iv) Tokens can expire and be revoked by the user or provider.
    Example Use in JavaScript (Frontend) -

    // You typically use OAuth with a redirect to the provider
    // Redirect user to Google's OAuth 2.0 authorization endpoint
    window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?
      client_id=YOUR_CLIENT_ID
      &redirect_uri=http://localhost:3000/callback
      &response_type=code
      &scope=email profile`;
    
    // After the user logs in and approves, Google redirects them back to your site with a code
    http://localhost:3000/callback?code=AUTH_CODE
    
    // Then your backend exchanges the code for an access token
    POST https://oauth2.googleapis.com/token
    {
      code: "AUTH_CODE",
      client_id: "YOUR_CLIENT_ID",
      client_secret: "YOUR_CLIENT_SECRET",
      redirect_uri: "http://localhost:3000/callback",
      grant_type: "authorization_code"
    }
    

    Benefits of OAuth -

    i) Secure, no password exposure.

    ii) Standardized and widely supported (Google, Facebook, GitHub, etc.).

    iii) Easily allows third-party integrations.

    iv) Grants limited, revocable access to user data.

  24. What is Cross-Site Scripting (XSS) in JavaScript?
    XSS (Cross-Site Scripting) is a security vulnerability that allows an attacker to inject malicious JavaScript code into a website, which then runs in another user’s browser.
    This can lead to -

    Stealing cookies/session tokens

    Logging keystrokes

    Defacing the page

    Redirecting users to malicious sites

    <!-- User input -->
    <p>Welcome, <span id="user-name"></span></p>
    
    <script>
      const userInput = "<script>alert('Hacked!')</script>";
      document.getElementById("user-name").innerHTML = userInput;
    </script>
    

    Types of XSS Attacks -
    i) Stored XSS
    Malicious script is saved on the server (e.g., in a database comment), and shown to users.

    ii) Reflected XSS
    Script is injected into a URL and reflected back in the response.

    example.com/search?q=<script>alert('XSS')</script>
    

    iii) DOM-based XSS
    JavaScript on the page dynamically inserts unsafe data into the DOM (client-side).
    How to Prevent XSS -
    i) Escape Output -

    Use textContent instead of innerHTML:

    document.getElementById("user-name").textContent = userInput;
    

    ii) Sanitize Input -
    Use libraries like: DOMPurify, Google Caja (legacy)

    const clean = DOMPurify.sanitize(dirtyHTML);
    

    iii) Use CSP (Content Security Policy) -

    CSP headers block unsafe inline scripts

    Content-Security-Policy: default-src 'self'; script-src 'self'
    

    iv) Validate Inputs -

    Always validate and sanitize user input on both frontend and backend.

  25. How does Cross-Site Request Forgery (CSRF) work?
    CSRF (Cross-Site Request Forgery) is an attack where a malicious website tricks a user's browser into making unwanted requests to a different site (where the user is authenticated), without their knowledge.
    CSRF tricks your browser into sending unauthorized requests as you.

    It abuses the trust a site has in the user (not the other way around).

    Use CSRF tokens, SameSite cookies, and re-authentication to stop it.

    So if you're logged into a site like bank.com, a malicious site can try to make your browser send a request as you, without you realizing.
    You’re logged into bank.com, which uses cookies to remember you. Then you visit evil.com, and it has this hidden form,

    <form action="https://bank.com/transfer" method="POST">
      <input type="hidden" name="amount" value="1000">
      <input type="hidden" name="to" value="attacker_account">
      <input type="submit">
    </form>
    
    <script>
      document.forms[0].submit(); // auto-submits the form
    </script>
    

    Your browser automatically sends your cookies with the request, and bank.com may process it as a legitimate transaction if it doesn’t check for CSRF.
    What Makes CSRF Dangerous? -

    i) Relies on the user’s authenticated session

    ii) The malicious request appears to come from the user

    iii) Can lead to data manipulation, money transfers, settings changes, etc.

    How to Prevent CSRF -
    i) CSRF Tokens (Anti-CSRF Tokens)

    Include a random, unique token in every sensitive form or request.

    Server generates a token and stores it in the user’s session

    Token is included in forms as a hidden input

    Server checks the token on each request

    <input type="hidden" name="csrfToken" value="RANDOM_TOKEN">
    
    // On server
    if (req.body.csrfToken !== session.csrfToken) {
      rejectRequest();
    }
    

    ii) Use SameSite Cookies

    Set cookies with SameSite=Strict or Lax to prevent them from being sent with cross-site requests

    Set-Cookie: sessionId=abc123; SameSite=Strict; Secure; HttpOnly
    

    iii) Require Re-authentication

    For sensitive actions (like transferring money), ask users to enter their password again.

  26. What is SQL Injection, and how can it affect JavaScript applications?

    SQL Injection is a major web security threat that developers need to understand, especially when using JavaScript on the backend (like with Node.js + a database).
    SQL Injection (SQLi) is a vulnerability where an attacker manipulates input fields to inject malicious SQL code into a database query.
    If the input is not properly sanitized, the attacker can: View sensitive data (e.g., passwords, emails),

    Modify or delete data, Bypass authentication, Drop entire tables.

    // Vulnerable Code (Unsafe)
    const username = req.body.username;
    const password = req.body.password;
    
    const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;
    db.query(query, function(err, results) {
      // ...
    });
    
    // Now if an attacker enters:
    // username: admin
    // password: ' OR '1'='1
    
    SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1'
    

    How to Prevent SQL Injection in JavaScript Apps -
    i) Use Prepared Statements / Parameterized Queries - Instead of embedding variables directly, pass them separately

    const query = "SELECT * FROM users WHERE username = $1 AND password = $2";
    client.query(query, [username, password]);
    

    ii) Use ORM Libraries -

    ORMs like Sequelize, Prisma, and TypeORM automatically handle parameterization

    const user = await User.findOne({ where: { username, password } });
    

    iii) Sanitize & Validate Input

    Use libraries like: validator, express-validator

    const { check } = require("express-validator");
    app.post("/login", [check("username").isAlphanumeric()], (req, res) => { ... });
    

    iv) Least Privilege Access

    Never give your database user full admin access. Restrict it to only what's necessary.