InterviewPitch
JavaScript & Algorithms

JavaScript Interview Questions with Answers

Most Asked JavaScript Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

This page provides a complete collection of JavaScript Interview Questions and Answers designed for frontend developers, full-stack engineers, and JavaScript enthusiasts preparing for technical interviews. JavaScript is the most popular programming language in the world, powering both client-side and server-side applications. Its event-driven, non-blocking nature makes it essential for modern web development. This guide covers beginner, intermediate, and advanced concepts including variables, functions, DOM manipulation, asynchronous programming, closures, ES6+ features, design patterns, and real-world coding problems.

Why JavaScript?

  • Runs everywhere – browsers, servers (Node.js), and mobile
  • Huge ecosystem with countless libraries and frameworks
  • Asynchronous programming with promises and async/await
  • Functional and object-oriented paradigms
  • Essential for modern web development – a must-have skill for interviews

Most Asked JavaScript Interview Questions

Beginner
1. What is JavaScript?

JavaScript is a high-level, interpreted scripting language used to create dynamic and interactive web pages. It is one of the core technologies of the World Wide Web.

  • Client-side: Runs in the browser
  • Server-side: Runs on Node.js
  • Event-driven: Responds to user actions
  • Prototype-based: Uses prototypal inheritance
  • Functional: Supports functional programming
java
// Hello World in JavaScript
console.log("Hello, World!");
Beginner
2. Is JavaScript a compiled or interpreted language?

JavaScript is an interpreted language, meaning it is executed line by line by the browser's JavaScript engine. However, modern JavaScript engines use Just-In-Time (JIT) compilation for performance optimization.

  • Interpreted: Executed at runtime without compilation
  • JIT Compilation: Modern engines compile hot code
  • Runtime: Code runs in the browser or Node.js
  • Dynamic: Types are determined at runtime
java
// Variables in JavaScript
var old = "function scoped";
let block = "block scoped";
const constant = "cannot be reassigned";

console.log(old);
console.log(block);
console.log(constant);
Beginner
3. What are variables in JavaScript?

Variables are containers for storing data values. JavaScript provides three ways to declare variables: var, let, and const.

  • var: Function-scoped, can be redeclared
  • let: Block-scoped, can be reassigned
  • const: Block-scoped, cannot be reassigned
  • Naming Rules: Must start with letter, _, or $
java
// Data Types in JavaScript
let str = "Hello";
let num = 42;
let bool = true;
let nul = null;
let undef = undefined;
let sym = Symbol("id");
let big = 9007199254740991n;
let obj = { name: "Alice", age: 25 };
let arr = [1, 2, 3];

console.log(typeof str);  // string
console.log(typeof num);  // number
console.log(typeof bool); // boolean
console.log(typeof nul);  // object (historical bug)
console.log(typeof undef); // undefined
console.log(typeof sym);  // symbol
console.log(typeof big);  // bigint
console.log(typeof obj);  // object
console.log(typeof arr);  // object
Beginner
4. Difference between var, let, and const?

The main differences between var, let, and const are scope, hoisting, and reassignment rules.

  • var: Function-scoped, hoisted, can be redeclared
  • let: Block-scoped, hoisted but not initialized (TDZ)
  • const: Block-scoped, cannot be reassigned
  • Best Practice: Use const by default, let when reassignment needed
java
// Functions in JavaScript
// Function declaration
function add(a, b) {
  return a + b;
}

// Function expression
const subtract = function(a, b) {
  return a - b;
};

// Arrow function
const multiply = (a, b) => a * b;

// Default parameters
function greet(name = "Guest") {
  return `Hello, ${name}!`;
}

// Rest parameters
function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}

console.log(add(5, 3));
console.log(subtract(10, 4));
console.log(multiply(6, 7));
console.log(greet("Alice"));
console.log(sum(1, 2, 3, 4, 5));
Beginner
5. What are data types in JavaScript?

JavaScript has 8 data types: 7 primitive types and 1 object type. Types are dynamic and can change at runtime.

  • Primitive Types: String, Number, Boolean, Null, Undefined, Symbol, BigInt
  • Reference Type: Object (arrays, functions, dates, etc.)
  • typeof: Operator to check data type
  • Dynamic Typing: Variables can hold any type
java
// Arrays in JavaScript
const arr = [1, 2, 3, 4, 5];

// Map - transform each element
const doubled = arr.map(x => x * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// Filter - select elements
const evens = arr.filter(x => x % 2 === 0);
console.log(evens); // [2, 4]

// Reduce - aggregate
const sum = arr.reduce((acc, x) => acc + x, 0);
console.log(sum); // 15

// forEach - iterate
arr.forEach(x => console.log(x));

// Find - find first match
const found = arr.find(x => x > 3);
console.log(found); // 4

// Some - check if any match
const hasEven = arr.some(x => x % 2 === 0);
console.log(hasEven); // true

// Every - check if all match
const allEven = arr.every(x => x % 2 === 0);
console.log(allEven); // false
Beginner
6. What is a function?

A function is a reusable block of code designed to perform a specific task. Functions can take parameters and return values.

  • Function Declaration: function name() {}
  • Function Expression: const func = function() {}
  • Arrow Function: () => {}
  • Parameters: Values passed to the function
  • Return: Value returned from the function
java
// Objects in JavaScript
// Object literal
const person = {
  name: "Alice",
  age: 25,
  city: "NYC",
  greet() {
    return `Hello, I'm ${this.name}`;
  }
};

console.log(person.name);
console.log(person.age);
console.log(person.greet());

// Object destructuring
const { name, age } = person;
console.log(name, age);

// Spread operator
const personCopy = { ...person, age: 26 };
console.log(personCopy);

// Object.keys, values, entries
console.log(Object.keys(person));
console.log(Object.values(person));
console.log(Object.entries(person));

// Object.assign
const merged = Object.assign({}, person, { country: "USA" });
console.log(merged);
Beginner
7. What is an array?

An array is a special object that stores multiple values in a single variable. Arrays are zero-indexed and can hold mixed data types.

  • Creation: [] or new Array()
  • Access: arr[index]
  • Methods: push, pop, shift, unshift, map, filter, reduce
  • Properties: length, prototype
java
// DOM Manipulation
// Select elements
const element = document.getElementById("myId");
const elements = document.getElementsByClassName("myClass");
const query = document.querySelector(".myClass");
const all = document.querySelectorAll("div");

// Create elements
const div = document.createElement("div");
div.textContent = "Hello";
div.className = "my-class";
div.id = "my-id";
document.body.appendChild(div);

// Event listeners
button.addEventListener("click", function(e) {
  console.log("Clicked!", e.target);
});

// Remove element
element.remove();

// Get/Set attributes
const value = element.getAttribute("data-value");
element.setAttribute("data-value", "new");

// Class manipulation
element.classList.add("active");
element.classList.remove("active");
element.classList.toggle("active");
element.classList.contains("active");

// InnerHTML vs textContent
element.innerHTML = "<span>HTML</span>";
element.textContent = "Plain text";
Beginner
8. What is an object?

An object is a collection of key-value pairs. Objects can contain properties and methods, and can be created using object literals or constructors.

  • Creation: or new Object()
  • Properties: Key-value pairs
  • Methods: Functions stored as properties
  • Access: obj.key or obj["key"]
java
// Events in JavaScript
// Click event
button.addEventListener("click", function(event) {
  console.log("Button clicked!", event);
});

// Mouse events
element.addEventListener("mouseenter", () => console.log("Mouse entered"));
element.addEventListener("mouseleave", () => console.log("Mouse left"));
element.addEventListener("mousemove", (e) => console.log(e.clientX, e.clientY));

// Keyboard events
document.addEventListener("keydown", (e) => {
  console.log(`Key ${e.key} pressed`);
});

// Form events
form.addEventListener("submit", (e) => {
  e.preventDefault();
  console.log("Form submitted");
});

input.addEventListener("change", (e) => {
  console.log("Value changed:", e.target.value);
});

input.addEventListener("input", (e) => {
  console.log("Input:", e.target.value);
});

// Event delegation
document.addEventListener("click", (e) => {
  if (e.target.matches(".button-class")) {
    console.log("Button clicked via delegation");
  }
});

// Remove event listener
button.removeEventListener("click", handler);
Beginner
9. What is DOM?

DOM (Document Object Model) is a programming interface for HTML documents. It represents the page structure as a tree of objects that can be manipulated with JavaScript.

  • Tree Structure: Document -> Elements -> Attributes
  • Node Types: Element, Text, Comment, Document
  • Methods: getElementById, querySelector, createElement
  • Properties: innerHTML, textContent, style
java
// Hoisting in JavaScript
// Function hoisting
console.log(add(2, 3)); // Works: 5
function add(a, b) {
  return a + b;
}

// Variable hoisting (var)
console.log(x); // undefined (not error)
var x = 10;
console.log(x); // 10

// Variable hoisting (let/const) - Temporal Dead Zone
// console.log(y); // ReferenceError
let y = 20;

// Function expression hoisting
// console.log(multiply(2, 3)); // TypeError
var multiply = function(a, b) {
  return a * b;
};

// Class hoisting
// const obj = new MyClass(); // ReferenceError
class MyClass {
  constructor() {
    this.name = "test";
  }
}
Beginner
10. What is an event?

An event is an action that occurs in the browser, such as user interaction or system events. JavaScript can respond to events using event listeners.

  • Mouse Events: click, hover, mousemove
  • Keyboard Events: keydown, keyup, keypress
  • Form Events: submit, change, input
  • Window Events: load, resize, scroll
java
// Closures in JavaScript
// Basic closure
function outer() {
  let count = 0;
  return function inner() {
    count++;
    return count;
  };
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2

// Closure with parameters
function multiplier(factor) {
  return function(number) {
    return number * factor;
  };
}
const double = multiplier(2);
console.log(double(5)); // 10

// Closure in loops
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 3, 3, 3
}

// Fix with let
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

// Private variables
function createCounter() {
  let count = 0;
  return {
    increment() { count++; },
    decrement() { count--; },
    getValue() { return count; }
  };
}
const counter2 = createCounter();
counter2.increment();
counter2.increment();
console.log(counter2.getValue()); // 2
Beginner
11. What is null?

null represents an intentional absence of value. It is a primitive value that indicates "no value" or "empty".

  • Type: typeof null returns "object" (historical bug)
  • Assignment: Can be assigned to variables
  • Comparison: null == undefined (true), null === undefined (false)
  • Use Case: Indicates missing object reference
java
// Scope in JavaScript
// Global scope
const globalVar = "global";

function testScope() {
  // Function scope
  var functionScoped = "function";

  // Block scope
  if (true) {
    let blockScoped = "block";
    const blockConst = "block const";
    var stillFunctionScoped = "still function";
  }
  // console.log(blockScoped); // ReferenceError
  console.log(stillFunctionScoped); // Works

  // Lexical scope
  function inner() {
    console.log(globalVar); // Access outer
    console.log(functionScoped); // Access outer
  }
  inner();
}

testScope();

// Module scope (ES modules)
// Each file has its own scope
// export const moduleVar = "module";

// Strict mode scope
"use strict";
// var x = 10; // Error if not declared
Beginner
12. What is undefined?

undefined is a primitive value that indicates a variable has been declared but not assigned a value. It is the default value of uninitialized variables.

  • Type: typeof undefined returns "undefined"
  • Assignment: Automatically assigned to uninitialized variables
  • Comparison: null == undefined (true), null === undefined (false)
  • Use Case: Indicates missing value or property
java
// == vs === in JavaScript
// == (loose equality) - type coercion
console.log(5 == "5");  // true
console.log(true == 1); // true
console.log(null == undefined); // true
console.log(0 == false); // true
console.log("" == false); // true

// === (strict equality) - no type coercion
console.log(5 === "5");  // false
console.log(true === 1); // false
console.log(null === undefined); // false
console.log(0 === false); // false
console.log("" === false); // false

// Object comparison
console.log({} === {}); // false
console.log([] === []); // false

// NaN comparison
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true
console.log(Object.is(NaN, NaN)); // true

// Best practice: Always use === unless you need type coercion
Beginner
13. What is typeof operator?

The typeof operator returns a string indicating the type of the operand. It is useful for type checking and debugging.

  • Syntax: typeof value
  • Returns: "string", "number", "boolean", "undefined", "object", "function", "symbol", "bigint"
  • Special Cases: typeof null === "object"
  • Use Case: Type checking and validation
java
// Callbacks in JavaScript
// Basic callback
function greet(name, callback) {
  console.log(`Hello, ${name}`);
  callback();
}

greet("Alice", function() {
  console.log("Callback executed!");
});

// Callback with error handling
function fetchData(callback) {
  try {
    const data = { id: 1, name: "Alice" };
    callback(null, data);
  } catch (error) {
    callback(error, null);
  }
}

fetchData((error, data) => {
  if (error) {
    console.error("Error:", error);
  } else {
    console.log("Data:", data);
  }
});

// Callback hell
doSomething(function(result1) {
  doSomethingElse(result1, function(result2) {
    doAnotherThing(result2, function(result3) {
      console.log("Done!", result3);
    });
  });
});

// Solution: Promises or async/await
Beginner
14. What is NaN?

NaN (Not-a-Number) is a special numeric value that indicates an invalid or unrepresentable number. It is returned when mathematical operations fail.

  • Type: typeof NaN === "number"
  • Comparison: NaN !== NaN (use isNaN() to check)
  • Causes: Invalid math, parse errors
  • Check: isNaN() or Number.isNaN()
java
// JSON in JavaScript
// JSON stringify and parse
const person = {
  name: "Alice",
  age: 25,
  hobbies: ["reading", "coding"],
  address: {
    city: "NYC",
    country: "USA"
  }
};

// Convert object to JSON string
const jsonString = JSON.stringify(person);
console.log(jsonString);
// {"name":"Alice","age":25,"hobbies":["reading","coding"],"address":{"city":"NYC","country":"USA"}}

// Convert JSON string to object
const jsonObject = JSON.parse(jsonString);
console.log(jsonObject);

// Pretty print
const pretty = JSON.stringify(person, null, 2);
console.log(pretty);

// Replacer function
const filtered = JSON.stringify(person, (key, value) => {
  return key === "age" ? undefined : value;
});
console.log(filtered);

// Reviver function
const revived = JSON.parse(jsonString, (key, value) => {
  return key === "age" ? value + 10 : value;
});
console.log(revived);
Beginner
15. What is strict mode?

Strict mode is a way to opt-in to a restricted variant of JavaScript that catches common coding errors and prevents unsafe actions.

  • Enable: "use strict" at top of file or function
  • Benefits: Catches silent errors, prevents unsafe actions
  • Changes: No global variables, no duplicate parameters
  • Best Practice: Always use strict mode
java
// Promises in JavaScript
// Creating a promise
const myPromise = new Promise((resolve, reject) => {
  // Async operation
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("Operation successful!");
    } else {
      reject("Operation failed!");
    }
  }, 1000);
});

// Using a promise
myPromise
  .then(result => {
    console.log("Success:", result);
    return "Next step";
  })
  .then(nextResult => {
    console.log("Next:", nextResult);
  })
  .catch(error => {
    console.error("Error:", error);
  })
  .finally(() => {
    console.log("Promise completed");
  });

// Promise.all
const promises = [
  Promise.resolve("First"),
  Promise.resolve("Second"),
  Promise.resolve("Third")
];

Promise.all(promises)
  .then(results => {
    console.log("All results:", results);
  })
  .catch(error => {
    console.error("One failed:", error);
  });

// Promise.race
Promise.race([
  new Promise(resolve => setTimeout(() => resolve("Fast"), 100)),
  new Promise(resolve => setTimeout(() => resolve("Slow"), 1000))
]).then(result => {
  console.log("Race winner:", result);
});

// Promise.allSettled
Promise.allSettled([
  Promise.resolve("Success"),
  Promise.reject("Failure")
]).then(results => {
  console.log("All settled:", results);
});
Intermediate
16. What is hoisting?

Hoisting is JavaScript's behavior of moving declarations to the top of their scope during compilation. Variables and functions are hoisted differently.

  • Function Declarations: Fully hoisted
  • var Variables: Hoisted but initialized as undefined
  • let/const Variables: Hoisted but in Temporal Dead Zone
  • Function Expressions: Not hoisted
java
// Async/Await in JavaScript
// Basic async/await
async function fetchData() {
  try {
    const data = await new Promise((resolve) => {
      setTimeout(() => resolve({ id: 1, name: "Alice" }), 1000);
    });
    console.log("Data:", data);
    return data;
  } catch (error) {
    console.error("Error:", error);
    throw error;
  }
}

// Using async function
fetchData();

// Async with multiple awaits
async function processData() {
  const data = await fetchData();
  const processed = await process(data);
  return processed;
}

// Parallel execution
async function parallelTasks() {
  const [user, posts] = await Promise.all([
    fetchUser(),
    fetchPosts()
  ]);
  return { user, posts };
}

// Error handling with async/await
async function safeFetch() {
  try {
    const response = await fetch("/api/data");
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error("Fetch failed:", error);
    return null;
  }
}
Intermediate
17. What is closure?

A closure is a function that has access to variables from its outer (enclosing) scope even after the outer function has returned. It "closes over" the variables.

  • Creation: Function defined inside another function
  • Access: Can access outer function's variables
  • Use Cases: Private variables, currying, event handlers
  • Memory: Variables remain in memory as long as closure exists
java
// Event Bubbling and Delegation
// HTML structure
/*
<div id="parent">
  <button id="child">Click me</button>
</div>
*/

// Event bubbling
document.getElementById("parent").addEventListener("click", () => {
  console.log("Parent clicked (bubbling)");
}, false); // false = bubbling (default)

document.getElementById("child").addEventListener("click", () => {
  console.log("Child clicked");
}, false);

// Event capturing
document.getElementById("parent").addEventListener("click", () => {
  console.log("Parent clicked (capturing)");
}, true); // true = capturing

// Stop propagation
document.getElementById("child").addEventListener("click", (e) => {
  e.stopPropagation();
  console.log("Child clicked - propagation stopped");
});

// Event delegation
document.getElementById("parent").addEventListener("click", (e) => {
  if (e.target.matches("button")) {
    console.log("Button clicked via delegation:", e.target.id);
  }
});

// Dynamic elements with delegation
document.addEventListener("click", (e) => {
  if (e.target.classList.contains("dynamic-btn")) {
    console.log("Dynamic button clicked");
  }
});
Intermediate
18. What is scope?

Scope determines the visibility and accessibility of variables in different parts of the code. JavaScript has global, function, and block scope.

  • Global Scope: Variables accessible everywhere
  • Function Scope: Variables accessible within function
  • Block Scope: Variables accessible within block (let/const)
  • Lexical Scope: Nested functions access parent scope
java
// this keyword in JavaScript
// Global context
console.log(this); // Window (browser)

// Function context (non-strict)
function showThis() {
  console.log(this); // Window/global
}
showThis();

// Function context (strict)
"use strict";
function showThisStrict() {
  console.log(this); // undefined
}
showThisStrict();

// Object method
const obj = {
  name: "Alice",
  greet() {
    console.log(this.name);
  }
};
obj.greet(); // Alice

// Arrow function (lexical this)
const arrowObj = {
  name: "Bob",
  greet: () => {
    console.log(this.name); // undefined (lexical this)
  }
};
arrowObj.greet();

// Constructor function
function Person(name) {
  this.name = name;
}
const person1 = new Person("Alice");
console.log(person1.name); // Alice

// call, apply, bind
function introduce(greeting) {
  console.log(`${greeting}, I'm ${this.name}`);
}
const user = { name: "Alice" };
introduce.call(user, "Hello"); // Hello, I'm Alice
introduce.apply(user, ["Hi"]); // Hi, I'm Alice
const bound = introduce.bind(user, "Hey");
bound(); // Hey, I'm Alice
Intermediate
19. Difference between == and ===?

== (loose equality) compares values after type coercion. === (strict equality) compares both value and type without coercion.

  • ==: Performs type coercion
  • ===: No type coercion
  • Best Practice: Always use === for predictable results
  • Object Comparison: Both compare references, not values
java
// Arrow Functions in JavaScript
// Basic arrow function
const add = (a, b) => a + b;

// Arrow function with multiple statements
const multiply = (a, b) => {
  const result = a * b;
  return result;
};

// Arrow function with no parameters
const greet = () => "Hello!";

// Arrow function with one parameter (parens optional)
const double = x => x * 2;

// Arrow function returning object
const createPerson = (name, age) => ({ name, age });

// Arrow function in array methods
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
const evens = numbers.filter(x => x % 2 === 0);
const sum = numbers.reduce((acc, x) => acc + x, 0);

// Arrow function with rest parameters
const sumAll = (...nums) => nums.reduce((acc, n) => acc + n, 0);

// Arrow function with destructuring
const printName = ({ name }) => console.log(name);

// Arrow function lexical this
function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++;
    console.log(this.seconds);
  }, 1000);
}
const timer = new Timer(); // Works correctly

// When NOT to use arrow functions
// 1. Object methods
const obj2 = {
  name: "Alice",
  greet: () => console.log(this.name) // Wrong!
};
// 2. Constructors
const Person2 = (name) => { this.name = name }; // Error
// 3. Event listeners (if you need 'this')
button.addEventListener("click", function() {
  console.log(this); // button element
});
Intermediate
20. What is a callback function?

A callback function is a function passed as an argument to another function and executed later, often after an asynchronous operation completes.

  • Definition: Function passed as parameter
  • Execution: Called inside the receiving function
  • Use Cases: Event handlers, async operations, array methods
  • Callback Hell: Nested callbacks leading to unreadable code
java
// Array Methods in JavaScript
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// map - transform each element
const doubled = arr.map(x => x * 2);
console.log(doubled);

// filter - select elements
const evens = arr.filter(x => x % 2 === 0);
console.log(evens);

// reduce - aggregate
const sum = arr.reduce((acc, x) => acc + x, 0);
console.log(sum);

// forEach - iterate
arr.forEach(x => console.log(x));

// find - find first match
const first = arr.find(x => x > 5);
console.log(first);

// findIndex - find index of first match
const index = arr.findIndex(x => x > 5);
console.log(index);

// some - check if any match
const hasEven = arr.some(x => x % 2 === 0);
console.log(hasEven);

// every - check if all match
const allEven = arr.every(x => x % 2 === 0);
console.log(allEven);

// includes - check if value exists
const includes = arr.includes(5);
console.log(includes);

// sort - sort array
const sorted = [...arr].sort((a, b) => a - b);
console.log(sorted);

// reverse - reverse array
const reversed = [...arr].reverse();
console.log(reversed);

// slice - create subarray
const sliced = arr.slice(2, 5);
console.log(sliced);

// splice - modify array
const spliced = [...arr];
spliced.splice(2, 3, 99, 100);
console.log(spliced);

// concat - merge arrays
const merged = arr.concat([11, 12, 13]);
console.log(merged);

// flat - flatten nested arrays
const nested = [1, [2, 3], [4, [5, 6]]];
const flattened = nested.flat(2);
console.log(flattened);
Intermediate
21. What is JSON?

JSON (JavaScript Object Notation) is a lightweight data interchange format that is easy for humans to read and write, and easy for machines to parse and generate.

  • Format: Key-value pairs, arrays, nested objects
  • Methods: JSON.stringify() and JSON.parse()
  • Data Types: Strings, numbers, booleans, arrays, objects, null
  • Use Cases: API communication, configuration files
java
// Object Methods in JavaScript
const person = {
  name: "Alice",
  age: 25,
  city: "NYC",
  hobbies: ["reading", "coding"]
};

// Object.keys - get keys
const keys = Object.keys(person);
console.log(keys);

// Object.values - get values
const values = Object.values(person);
console.log(values);

// Object.entries - get key-value pairs
const entries = Object.entries(person);
console.log(entries);

// Object.fromEntries - create from entries
const fromEntries = Object.fromEntries(entries);
console.log(fromEntries);

// Object.assign - merge objects
const additional = { country: "USA", age: 26 };
const merged = Object.assign({}, person, additional);
console.log(merged);

// Object.freeze - prevent modifications
const frozen = Object.freeze({ name: "Alice" });
// frozen.name = "Bob"; // Error in strict mode

// Object.seal - prevent adding/removing properties
const sealed = Object.seal({ name: "Alice" });
sealed.name = "Bob"; // Works
// sealed.age = 25; // Error

// Object.hasOwn - check own property
console.log(Object.hasOwn(person, "name")); // true
console.log(Object.hasOwn(person, "toString")); // false

// Object.getOwnPropertyNames
const ownProps = Object.getOwnPropertyNames(person);
console.log(ownProps);
Intermediate
22. What is promise?

A promise is an object representing the eventual completion or failure of an asynchronous operation. It provides a cleaner way to handle async code than callbacks.

  • States: pending, fulfilled, rejected
  • Methods: then(), catch(), finally()
  • Chaining: Sequential async operations
  • Static Methods: Promise.all(), Promise.race()
java
// Destructuring in JavaScript
// Array destructuring
const arr = [1, 2, 3, 4, 5];
const [first, second, ...rest] = arr;
console.log(first, second, rest); // 1, 2, [3, 4, 5]

// Swap variables
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1

// Object destructuring
const person = { name: "Alice", age: 25, city: "NYC" };
const { name, age } = person;
console.log(name, age);

// Rename variables
const { name: fullName, age: years } = person;
console.log(fullName, years);

// Default values
const { country = "USA" } = person;
console.log(country);

// Nested destructuring
const user = {
  id: 1,
  profile: {
    name: "Alice",
    address: {
      city: "NYC",
      zip: "10001"
    }
  }
};
const { profile: { address: { city } } } = user;
console.log(city);

// Function parameter destructuring
function printPerson({ name, age }) {
  console.log(`${name} is ${age} years old`);
}
printPerson(person);

// Array destructuring with rest
const [head, ...tail] = [1, 2, 3, 4];
console.log(head, tail);
Intermediate
23. What is async/await?

Async/await is modern syntax for working with promises, making asynchronous code look and behave like synchronous code.

  • async: Declares an asynchronous function
  • await: Waits for a promise to resolve
  • Error Handling: Use try/catch
  • Readability: Cleaner than promise chains
java
// Spread and Rest Operators
// Spread operator in arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined);

// Copy array
const copy = [...arr1];
console.log(copy);

// Spread in function calls
const numbers = [1, 2, 3, 4, 5];
const max = Math.max(...numbers);
console.log(max);

// Spread in objects
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const mergedObj = { ...obj1, ...obj2 };
console.log(mergedObj);

// Rest parameters
function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3, 4));

// Rest in destructuring
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head, tail);

// Rest in object destructuring
const { a, ...restObj } = { a: 1, b: 2, c: 3 };
console.log(a, restObj);

// Spread with strings
const chars = [..."hello"];
console.log(chars); // ['h', 'e', 'l', 'l', 'o']

// Spread with sets
const set = new Set([1, 2, 3]);
const arr = [...set];
console.log(arr);
Intermediate
24. What is event bubbling?

Event bubbling is the propagation of an event from the target element up through the DOM tree to the root. It allows parent elements to handle events from children.

  • Direction: Child → Parent → Root
  • Stop: stopPropagation()
  • Prevent: preventDefault()
  • Capturing Phase: Opposite direction (root → child)
java
// Template Literals in JavaScript
// Basic template literal
const name = "Alice";
const greeting = `Hello, ${name}!`;
console.log(greeting);

// Multi-line strings
const multiLine = `
  This is a
  multi-line
  string
`;
console.log(multiLine);

// Expression interpolation
const a = 5, b = 10;
console.log(`${a} + ${b} = ${a + b}`);

// Nested templates
const isAdmin = true;
const message = `User is ${isAdmin ? `an admin` : `a regular user`}`;
console.log(message);

// Tagged templates
function highlight(strings, ...values) {
  return strings.reduce((acc, str, i) => {
    return acc + str + (values[i] ? `<b>${values[i]}</b>` : "");
  }, "");
}
const highlighted = highlight`Hello ${name}, you are ${age} years old`;
console.log(highlighted);

// Raw strings
const raw = String.raw`Hello\nWorld`;
console.log(raw); // Hello\nWorld (not escaped)

// Template literals in loops
const items = ["Apple", "Banana", "Orange"];
const list = items.map(item => `<li>${item}</li>`).join("");
console.log(list);
Intermediate
25. What is event delegation?

Event delegation is a technique where a parent element handles events for its children using event bubbling. It reduces the number of event listeners needed.

  • Benefits: Fewer listeners, handles dynamic elements
  • Implementation: Add listener to parent
  • Target: event.target to identify child
  • Use Cases: Lists, tables, dynamic content
java
// Map and Set in JavaScript
// Map - key-value pairs (any keys)
const map = new Map();

// Set values
map.set("name", "Alice");
map.set(42, "answer");
map.set({ id: 1 }, "object key");

// Get values
console.log(map.get("name")); // Alice
console.log(map.get(42)); // answer

// Check key existence
console.log(map.has("name")); // true

// Delete key
map.delete("name");

// Iterate map
map.forEach((value, key) => {
  console.log(key, value);
});

// Map from array
const mapFromArray = new Map([
  ["name", "Alice"],
  ["age", 25]
]);
console.log(mapFromArray);

// Set - unique values
const set = new Set();

// Add values
set.add(1);
set.add(2);
set.add(3);
set.add(3); // Duplicate ignored

console.log(set.has(2)); // true
console.log(set.size); // 3

// Delete value
set.delete(2);

// Iterate set
set.forEach(value => console.log(value));

// Set from array
const setFromArray = new Set([1, 2, 3, 3, 4]);
console.log(setFromArray); // Set(4) {1, 2, 3, 4}

// Remove duplicates from array
const arr = [1, 2, 3, 3, 4, 4, 5];
const unique = [...new Set(arr)];
console.log(unique);
Intermediate
26. What is this keyword?

The this keyword refers to the object that is currently executing the function. Its value depends on how the function is called.

  • Global: Window (browser) or Global (Node)
  • Method: Owner object
  • Constructor: New instance
  • Arrow Function: Lexical (parent's this)
java
// WeakMap and WeakSet
// WeakMap - keys must be objects, garbage collected
const weakMap = new WeakMap();
const obj = { id: 1 };
weakMap.set(obj, "value");
console.log(weakMap.get(obj));

// WeakMap does not prevent garbage collection
// If obj is deleted, the entry is automatically removed

// WeakSet - values must be objects
const weakSet = new WeakSet();
const obj2 = { id: 2 };
weakSet.add(obj2);
console.log(weakSet.has(obj2));

// Use cases: caching, private data
// Private data with WeakMap
const privateData = new WeakMap();

class Person {
  constructor(name) {
    privateData.set(this, { name });
  }
  
  getName() {
    return privateData.get(this).name;
  }
}

const p = new Person("Alice");
console.log(p.getName());

// WeakMap for DOM element metadata
const elementData = new WeakMap();
const button = document.createElement("button");
elementData.set(button, { clicks: 0 });
button.addEventListener("click", () => {
  const data = elementData.get(button);
  data.clicks++;
  console.log(`Clicked ${data.clicks} times`);
});
Intermediate
27. What is arrow function?

Arrow functions provide a shorter syntax for writing functions and have lexical this binding. They are commonly used for callbacks and functional programming.

  • Syntax: () => {}
  • this: Lexical binding (parent's this)
  • No arguments: Use rest parameters
  • No constructor: Cannot be used with new
java
// Symbol in JavaScript
// Creating symbols
const sym1 = Symbol();
const sym2 = Symbol("description");
const sym3 = Symbol("description");

console.log(sym2 === sym3); // false

// Symbols as object keys
const uniqueKey = Symbol("key");
const obj = {
  [uniqueKey]: "secret value",
  regular: "normal"
};
console.log(obj[uniqueKey]); // secret value

// Symbol.for - global symbol registry
const globalSym1 = Symbol.for("shared");
const globalSym2 = Symbol.for("shared");
console.log(globalSym1 === globalSym2); // true

// Well-known symbols
// Symbol.iterator
const iterableObj = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  }
};
for (const value of iterableObj) {
  console.log(value);
}

// Symbol.toStringTag
const customObj = {
  [Symbol.toStringTag]: "CustomObject"
};
console.log(Object.prototype.toString.call(customObj)); // [object CustomObject]

// Symbol.toPrimitive
const primitiveObj = {
  [Symbol.toPrimitive](hint) {
    if (hint === "number") return 42;
    if (hint === "string") return "forty-two";
    return null;
  }
};
console.log(+primitiveObj); // 42
console.log(`${primitiveObj}`); // forty-two
Intermediate
28. What is map()?

The map() method creates a new array by applying a function to each element of the original array. It does not modify the original array.

  • Syntax: array.map(callback)
  • Return: New array with transformed elements
  • Parameters: element, index, array
  • Chaining: Can chain with other array methods
java
// Iterators and Generators
// Custom iterator
const range = {
  start: 0,
  end: 5,
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (const num of range) {
  console.log(num);
}

// Generator function
function* numberGenerator() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
}

const gen = numberGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3

// Infinite generator
function* infiniteGenerator() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

const infinite = infiniteGenerator();
console.log(infinite.next().value); // 0
console.log(infinite.next().value); // 1
console.log(infinite.next().value); // 2

// Generator with return
function* generatorWithReturn() {
  yield 1;
  yield 2;
  return 3;
  yield 4;
}

const gen2 = generatorWithReturn();
console.log(gen2.next()); // { value: 1, done: false }
console.log(gen2.next()); // { value: 2, done: false }
console.log(gen2.next()); // { value: 3, done: true }

// Generator delegation
function* generator1() {
  yield 1;
  yield 2;
}

function* generator2() {
  yield* generator1();
  yield 3;
  yield 4;
}

for (const value of generator2()) {
  console.log(value); // 1, 2, 3, 4
}
Intermediate
29. What is filter()?

The filter() method creates a new array with elements that pass a test implemented by a provided function. It does not modify the original array.

  • Syntax: array.filter(callback)
  • Return: New array with elements that pass the test
  • Parameters: element, index, array
  • Use Cases: Removing unwanted elements
java
// Classes in JavaScript
// Class definition
class Person {
  // Constructor
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  // Instance method
  greet() {
    return `Hello, I'm ${this.name}`;
  }
  
  // Getter
  get fullName() {
    return `${this.name} (Age: ${this.age})`;
  }
  
  // Setter
  set fullName(value) {
    this.name = value;
  }
  
  // Static method
  static createAnonymous() {
    return new Person("Anonymous", 0);
  }
  
  // Private field (ES2022)
  #privateField = "private";
  
  // Private method
  #privateMethod() {
    return "private method";
  }
}

// Inheritance
class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }
  
  // Override method
  greet() {
    return `${super.greet()} and I'm in grade ${this.grade}`;
  }
}

const person = new Person("Alice", 25);
console.log(person.greet());
console.log(person.fullName);
const student = new Student("Bob", 20, "A");
console.log(student.greet());

// Static method
const anonymous = Person.createAnonymous();
console.log(anonymous.greet());

// Class expression
const Animal = class {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} makes a sound`;
  }
};
Intermediate
30. What is reduce()?

The reduce() method reduces an array to a single value by executing a reducer function on each element. It accumulates the result.

  • Syntax: array.reduce(callback, initialValue)
  • Parameters: accumulator, current, index, array
  • Return: Single accumulated value
  • Use Cases: Sum, average, object grouping
java
// Modules in JavaScript (ES6)
// Exporting
// math.js
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export function subtract(a, b) { return a - b; }

// Default export
export default class Calculator {
  multiply(a, b) { return a * b; }
}

// Named export with alias
export { add as sum };

// Importing
// main.js
import Calculator, { PI, add, subtract, sum } from './math.js';

// Import all
import * as MathUtils from './math.js';

// Import with alias
import { add as addition } from './math.js';

// Dynamic import
const module = await import('./math.js');

// Import for side effects
import './styles.css';

// Re-exporting
export { add, subtract } from './math.js';
export * from './math.js';

// Module scope
// Variables in modules are scoped to the module
// "use strict" is applied automatically

// Module loading
// <script type="module" src="main.js"></script>
// <script type="module">
//   import { add } from './math.js';
// </script>

// Top-level await
const data = await fetch('/api/data');
const json = await data.json();
console.log(json);
Advanced
31. What is prototype?

The prototype is a mechanism for sharing properties and methods between objects in JavaScript. Every function has a prototype property.

  • Prototype Chain: Objects inherit from their prototype
  • __proto__: Object's prototype reference
  • Prototype Property: Object.prototype
  • Inheritance: Shared methods and properties
java
// Prototypes in JavaScript
// Prototype chain
const parent = { name: "Parent", greet() { return "Hello"; } };
const child = Object.create(parent);
child.name = "Child";
console.log(child.name); // Child
console.log(child.greet()); // Hello (inherited)

// Constructor function
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.greet = function() {
  return `Hello, I'm ${this.name}`;
};

const person2 = new Person("Alice", 25);
console.log(person2.greet());

// Prototype inheritance
function Student(name, age, grade) {
  Person.call(this, name, age);
  this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.study = function() {
  return `${this.name} is studying`;
};

const student2 = new Student("Bob", 20, "A");
console.log(student2.greet());
console.log(student2.study());

// hasOwnProperty
console.log(person2.hasOwnProperty("name")); // true
console.log(person2.hasOwnProperty("greet")); // false

// __proto__ (deprecated)
console.log(person2.__proto__ === Person.prototype); // true

// Object.getPrototypeOf
console.log(Object.getPrototypeOf(person2) === Person.prototype); // true

// instanceof
console.log(person2 instanceof Person); // true
console.log(person2 instanceof Object); // true
Advanced
32. What is prototypal inheritance?

Prototypal inheritance is a form of inheritance where objects inherit properties and methods from other objects (their prototype).

  • Prototype Chain: Objects inherit from their prototype
  • Object.create: Create objects with specific prototype
  • Classes: ES6 class syntax wraps prototype inheritance
  • Mixins: Combining multiple prototypes
java
// Prototypal Inheritance
// Object.create for inheritance
const animal = {
  speak() {
    return `${this.name} makes a sound`;
  },
  eat() {
    return `${this.name} is eating`;
  }
};

const dog = Object.create(animal);
dog.name = "Rex";
dog.speak = function() {
  return `${this.name} barks!`;
};

console.log(dog.speak()); // Rex barks!
console.log(dog.eat()); // Rex is eating

// Multiple inheritance with mixins
const flyable = {
  fly() {
    return `${this.name} is flying`;
  }
};
const swimmable = {
  swim() {
    return `${this.name} is swimming`;
  }
};

function mixin(target, ...sources) {
  Object.assign(target, ...sources);
}

const duck = { name: "Donald" };
mixin(duck, flyable, swimmable);
console.log(duck.fly()); // Donald is flying
console.log(duck.swim()); // Donald is swimming

// Class-based inheritance (ES6)
class Animal2 {
  constructor(name) {
    this.name = name;
  }
  speak() {
    return `${this.name} makes a sound`;
  }
}

class Dog2 extends Animal2 {
  speak() {
    return `${this.name} barks!`;
  }
}

const rex = new Dog2("Rex");
console.log(rex.speak()); // Rex barks!
Advanced
33. What is currying?

Currying is the technique of transforming a function that takes multiple arguments into a sequence of functions that each take a single argument.

  • Benefits: Partial application, function composition
  • Implementation: Return functions for each argument
  • Use Cases: Configuration, reusable functions
  • Arrow Functions: a => b => a + b
java
// Currying in JavaScript
// Basic currying
function add(a) {
  return function(b) {
    return a + b;
  };
}
const add5 = add(5);
console.log(add5(3)); // 8
console.log(add(5)(3)); // 8

// Currying with arrow functions
const multiply = a => b => a * b;
const multiplyBy2 = multiply(2);
console.log(multiplyBy2(5)); // 10

// Currying with multiple arguments
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function(...more) {
      return curried.apply(this, args.concat(more));
    };
  };
}

function sum(a, b, c) {
  return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6

// Practical example: discount calculation
function calculateDiscount(discount) {
  return function(price) {
    return price * (1 - discount);
  };
}

const tenPercentOff = calculateDiscount(0.10);
const twentyPercentOff = calculateDiscount(0.20);
console.log(tenPercentOff(100)); // 90
console.log(twentyPercentOff(100)); // 80

// Partial application
function greet(greeting, name) {
  return `${greeting}, ${name}!`;
}
const sayHello = greet.bind(null, "Hello");
console.log(sayHello("Alice")); // Hello, Alice!
Advanced
34. What is debouncing?

Debouncing is a technique that limits how often a function is called by delaying its execution until after a specified time has passed since the last call.

  • Use Cases: Search input, resize events
  • Implementation: setTimeout and clearTimeout
  • Delay: Function executes after delay
  • Benefits: Reduces unnecessary function calls
java
// Debouncing in JavaScript
// Debounce function
function debounce(func, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}

// Usage: debounced search
const searchInput = document.getElementById("search");
const debouncedSearch = debounce((query) => {
  console.log("Searching for:", query);
  // API call here
}, 500);

searchInput.addEventListener("input", (e) => {
  debouncedSearch(e.target.value);
});

// Debounce with immediate execution
function debounceImmediate(func, delay, immediate = false) {
  let timeoutId;
  return function(...args) {
    const callNow = immediate && !timeoutId;
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      timeoutId = null;
      if (!immediate) {
        func.apply(this, args);
      }
    }, delay);
    if (callNow) {
      func.apply(this, args);
    }
  };
}

// Usage: debounced save
const saveButton = document.getElementById("save");
const debouncedSave = debounceImmediate(() => {
  console.log("Saving data...");
}, 1000, true);

saveButton.addEventListener("click", debouncedSave);

// Debounce with leading and trailing options
function debounceAdvanced(func, delay, options = { leading: false, trailing: true }) {
  let timeoutId;
  let lastCallTime;
  return function(...args) {
    const now = Date.now();
    const isFirstCall = !lastCallTime;
    lastCallTime = now;
    
    if (options.leading && isFirstCall) {
      func.apply(this, args);
      return;
    }
    
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      if (options.trailing) {
        func.apply(this, args);
      }
    }, delay);
  };
}
Advanced
35. What is throttling?

Throttling is a technique that limits how often a function can be called by ensuring it executes at most once in a specified time period.

  • Use Cases: Scroll events, animations
  • Implementation: Timestamps or setTimeout
  • Rate Limit: Function executes at fixed intervals
  • Benefits: Prevents performance issues
java
// Throttling in JavaScript
// Throttle function
function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// Usage: throttled scroll
const throttledScroll = throttle(() => {
  console.log("Scroll position:", window.scrollY);
}, 200);

window.addEventListener("scroll", throttledScroll);

// Throttle with trailing execution
function throttleTrailing(func, limit) {
  let inThrottle;
  let lastFunc;
  let lastRan;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      lastRan = Date.now();
      inThrottle = true;
      setTimeout(() => {
        inThrottle = false;
      }, limit);
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(() => {
        if ((Date.now() - lastRan) >= limit) {
          func.apply(this, args);
          lastRan = Date.now();
        }
      }, limit - (Date.now() - lastRan));
    }
  };
}

// Throttle with leading and trailing options
function throttleAdvanced(func, limit, options = { leading: true, trailing: true }) {
  let inThrottle;
  let lastRan;
  let lastFunc;
  
  return function(...args) {
    const now = Date.now();
    
    if (!inThrottle) {
      if (options.leading) {
        func.apply(this, args);
      }
      lastRan = now;
      inThrottle = true;
      setTimeout(() => {
        inThrottle = false;
        if (options.trailing && lastFunc) {
          func.apply(this, args);
          lastFunc = null;
        }
      }, limit);
    } else {
      if (options.trailing) {
        clearTimeout(lastFunc);
        lastFunc = setTimeout(() => {
          func.apply(this, args);
        }, limit - (now - lastRan));
      }
    }
  };
}
Advanced
36. What is event loop?

The event loop is a mechanism that handles asynchronous callbacks in JavaScript. It continuously checks the call stack and task queues to execute pending tasks.

  • Call Stack: Synchronous execution
  • Task Queue: Asynchronous callbacks
  • Microtasks: Promise callbacks (higher priority)
  • Macrotasks: setTimeout, setInterval, events
java
// Event Loop in JavaScript
// Basic event loop example
console.log("Start");

setTimeout(() => {
  console.log("Timeout callback");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise callback");
});

console.log("End");

// Output:
// Start
// End
// Promise callback
// Timeout callback

// Microtasks vs Macrotasks
console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve().then(() => {
  console.log("3");
});

console.log("4");

// Output: 1, 4, 3, 2
// Microtasks (Promises) execute before Macrotasks (setTimeout)

// Event loop with async/await
async function asyncFunction() {
  console.log("A");
  await Promise.resolve();
  console.log("B");
}

console.log("C");
asyncFunction();
console.log("D");

// Output: C, A, D, B

// Event loop visualization
setTimeout(() => console.log("Timeout 1"), 0);
setTimeout(() => console.log("Timeout 2"), 100);

Promise.resolve()
  .then(() => console.log("Promise 1"))
  .then(() => console.log("Promise 2"));

console.log("Sync code");

// Output: Sync code, Promise 1, Promise 2, Timeout 1, Timeout 2
Advanced
37. What are call(), apply(), bind()?

call(), apply(), and bind() are methods that allow you to control the value of this in a function.

  • call(): Calls function with specific this and arguments
  • apply(): Same as call but takes array of arguments
  • bind(): Creates new function with specific this
  • Use Cases: Method borrowing, partial application
java
// call(), apply(), bind() Methods
const person = {
  name: "Alice",
  greet(greeting) {
    return `${greeting}, I'm ${this.name}`;
  }
};

// call()
const bob = { name: "Bob" };
console.log(person.greet.call(bob, "Hello")); // Hello, I'm Bob

// apply()
console.log(person.greet.apply(bob, ["Hi"])); // Hi, I'm Bob

// bind()
const greetBob = person.greet.bind(bob);
console.log(greetBob("Hey")); // Hey, I'm Bob

// Partial application with bind
function multiply(a, b) {
  return a * b;
}
const double = multiply.bind(null, 2);
console.log(double(5)); // 10

// Borrowing methods
const arr = [1, 2, 3];
const arrLike = { 0: "a", 1: "b", 2: "c", length: 3 };
const result = Array.prototype.slice.call(arrLike);
console.log(result); // ['a', 'b', 'c']

// Using apply with Math.max
const numbers = [1, 5, 3, 9, 2];
const max = Math.max.apply(null, numbers);
console.log(max); // 9

// Using bind with event handlers
class Button {
  constructor(text) {
    this.text = text;
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    console.log(`${this.text} clicked`);
  }
}

const btn = new Button("Save");
// btn.handleClick(); // Save clicked
Advanced
38. What is memory leak?

A memory leak occurs when memory that is no longer needed is not released. JavaScript's garbage collector handles most memory, but leaks can still happen.

  • Causes: Global variables, forgotten timers, closures
  • Detection: Browser DevTools memory profiler
  • Prevention: Clean up, use WeakMap/WeakSet
  • Impact: Performance degradation, crashes
java
// Memory Leaks in JavaScript
// Common memory leak sources

// 1. Global variables
function leak() {
  globalVar = "I'm global"; // Implicit global
}
leak();

// 2. Forgotten timers
function timerLeak() {
  let element = document.getElementById("leak");
  setInterval(() => {
    console.log(element.id);
  }, 1000);
}

// 3. Event listeners not removed
function eventListenerLeak() {
  let element = document.getElementById("leak");
  element.addEventListener("click", function handler() {
    console.log("Clicked");
  });
  // element removed but listener remains
}

// 4. Closures holding references
function closureLeak() {
  let largeData = new Array(1000000);
  return function() {
    console.log(largeData.length);
  };
}
const leakyClosure = closureLeak();

// 5. DOM references
function domReferenceLeak() {
  let element = document.getElementById("leak");
  document.body.removeChild(element);
  // element still holds reference
}

// Prevention
// 1. Use 'use strict'
// 2. Clear timers
clearTimeout(timeoutId);
clearInterval(intervalId);

// 3. Remove event listeners
element.removeEventListener("click", handler);

// 4. Nullify references
element = null;

// 5. Use WeakMap and WeakSet
const cache = new WeakMap();
const obj = {};
cache.set(obj, "value");
// When obj is garbage collected, entry is removed
Advanced
39. What is module in JavaScript?

A module is a reusable piece of code that exports specific functionality and can be imported by other modules. ES6 introduced native module support.

  • Export: export keyword
  • Import: import keyword
  • Default Export: export default
  • Dynamic Import: import() for lazy loading
java
// ES6 Modules Advanced
// module.js
export const data = { id: 1, name: "Alice" };
export function process() { return "processing"; }

// Default export
export default class User {
  constructor(name) { this.name = name; }
}

// Import with alias
import User, { data as userData, process } from './module.js';

// Re-exporting
export { data, process } from './module.js';
export * from './module.js';

// Import namespace
import * as Module from './module.js';
console.log(Module.data);

// Dynamic import (lazy loading)
async function loadModule() {
  const module = await import('./module.js');
  console.log(module.default);
}

// Module import for side effects
import './styles.css';

// JSON imports (with import assertions)
import data from './data.json' assert { type: "json" };
console.log(data);

// Module variables
console.log(import.meta.url); // Current module URL
console.log(import.meta); // Module metadata

// Exporting types
export interface User {
  name: string;
  age: number;
}

// Export with dynamic binding
export let counter = 0;
export function increment() {
  counter++;
}
Advanced
40. What is IIFE?

IIFE (Immediately Invoked Function Expression) is a function that is defined and executed immediately after creation. It creates a private scope.

  • Syntax: (function() )()
  • Purpose: Private variables, module pattern
  • Arrow IIFE: (() => {})()
  • Use Cases: Module pattern, avoiding global scope
java
// IIFE (Immediately Invoked Function Expression)
// Basic IIFE
(function() {
  console.log("IIFE executed!");
})();

// IIFE with parameters
(function(name) {
  console.log(`Hello, ${name}!`);
})("Alice");

// Arrow function IIFE
(() => {
  console.log("Arrow IIFE");
})();

// IIFE with return value
const result = (function() {
  let count = 0;
  return {
    increment() { count++; },
    decrement() { count--; },
    getCount() { return count; }
  };
})();

// IIFE for private variables
const counterModule = (function() {
  let privateCounter = 0;
  
  function changeBy(val) {
    privateCounter += val;
  }
  
  return {
    increment() { changeBy(1); },
    decrement() { changeBy(-1); },
    value() { return privateCounter; }
  };
})();

// IIFE for module pattern
const myModule = (function() {
  // Private
  const privateData = [];
  
  // Public
  return {
    add(item) {
      privateData.push(item);
    },
    get() {
      return [...privateData];
    }
  };
})();

// IIFE in loops (pre-ES6)
for (var i = 0; i < 3; i++) {
  (function(index) {
    setTimeout(() => {
      console.log(index);
    }, 100);
  })(i);
}

// With let (no IIFE needed)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
Coding Round
41. Reverse a string

To reverse a string, you can split it into an array, reverse the array, and join it back together. This is a common interview question.

  • Method: str.split('').reverse().join('')
  • Alternative: [...str].reverse().join('')
  • Loop Method: Iterate from end to start
  • Recursive: Recursively reverse the string
java
// Reverse a string
function reverseString(str) {
  return str.split('').reverse().join('');
}
console.log(reverseString("hello")); // "olleh"

// Alternative with spread
const reverse = str => [...str].reverse().join('');
console.log(reverse("world")); // "dlrow"
Coding Round
42. Check palindrome

A palindrome is a word that reads the same backward as forward. Check by reversing the string and comparing with the original.

  • Method: str === str.split('').reverse().join('')
  • Case Insensitive: Convert to lowercase
  • Ignore Non-alphanumeric: Use regex
  • Two-pointer: Compare from both ends
java
// Check palindrome
function isPalindrome(str) {
  const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
  return cleaned === cleaned.split('').reverse().join('');
}
console.log(isPalindrome("racecar")); // true
console.log(isPalindrome("hello")); // false
Coding Round
43. Find max in array

Find the maximum value in an array using Math.max with the spread operator, or by iterating through the array.

  • Method: Math.max(...arr)
  • Loop: arr.reduce((max, x) => Math.max(max, x))
  • Sorted: arr[arr.length - 1]
  • Edge Cases: Handle empty array
java
// Find max in array
function findMax(arr) {
  return Math.max(...arr);
}
console.log(findMax([1, 5, 3, 9, 2])); // 9

// Without spread
function findMaxManual(arr) {
  return arr.reduce((max, curr) => curr > max ? curr : max, -Infinity);
}
Coding Round
44. Remove duplicates

Remove duplicates from an array using Set, filter, or reduce. The Set method is the simplest and most efficient.

  • Set: [...new Set(arr)]
  • Filter: arr.filter((item, i) => arr.indexOf(item) === i)
  • Reduce: arr.reduce((acc, x) => acc.includes(x) ? acc : [...acc, x], [])
  • Use Cases: Unique values, deduplication
java
// Remove duplicates
function removeDuplicates(arr) {
  return [...new Set(arr)];
}
console.log(removeDuplicates([1, 2, 2, 3, 3, 4])); // [1, 2, 3, 4]

// With filter
function removeDuplicatesFilter(arr) {
  return arr.filter((item, index) => arr.indexOf(item) === index);
}
Coding Round
45. Merge arrays

Merge two arrays using spread operator or concat method. Both create a new array without modifying the originals.

  • Spread: [...arr1, ...arr2]
  • Concat: arr1.concat(arr2)
  • Push: arr1.push(...arr2) (modifies arr1)
  • Unique Merge: [...new Set([...arr1, ...arr2])]
java
// Merge arrays
function mergeArrays(arr1, arr2) {
  return [...arr1, ...arr2];
}
console.log(mergeArrays([1, 2], [3, 4])); // [1, 2, 3, 4]

// Alternative
function mergeArraysConcat(arr1, arr2) {
  return arr1.concat(arr2);
}
Coding Round
46. Convert string to number

Convert a string to a number using Number(), parseInt(), parseFloat(), or the unary plus operator.

  • Number(): Number(str)
  • parseInt(): parseInt(str, 10)
  • parseFloat(): parseFloat(str)
  • Unary Plus: +str
java
// Convert string to number
function stringToNumber(str) {
  return Number(str);
}
console.log(stringToNumber("42")); // 42

// Alternative
function stringToNumberParse(str) {
  return parseInt(str, 10);
}
console.log(stringToNumberParse("42")); // 42
Coding Round
47. Loop through object

Loop through an object's properties using for...in, Object.keys(), Object.values(), or Object.entries().

  • for...in: for (let key in obj)
  • Object.keys(): Object.keys(obj).forEach(key => ...)
  • Object.values(): Object.values(obj).forEach(value => ...)
  • Object.entries(): Object.entries(obj).forEach(([key, value]) => ...)
java
// Loop through object
function loopObject(obj) {
  // for...in
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      console.log(key, obj[key]);
    }
  }
  
  // Object.keys
  Object.keys(obj).forEach(key => {
    console.log(key, obj[key]);
  });
  
  // Object.entries
  Object.entries(obj).forEach(([key, value]) => {
    console.log(key, value);
  });
}

const person = { name: "Alice", age: 25 };
loopObject(person);
Coding Round
48. Delay function execution

Delay function execution using setTimeout, async/await with sleep, or setInterval for repeated execution.

  • setTimeout: setTimeout(func, ms)
  • Promise: new Promise(resolve => setTimeout(resolve, ms))
  • async/await: await sleep(ms)
  • setInterval: setInterval(func, ms)
java
// Delay function execution
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function delayedExecution() {
  console.log("Start");
  await delay(2000);
  console.log("After 2 seconds");
}

delayedExecution();

// SetTimeout
function delayCallback(callback, ms) {
  setTimeout(callback, ms);
}

delayCallback(() => console.log("Delayed"), 1000);
Coding Round
49. Fetch API example

Fetch API provides a modern way to make HTTP requests. It returns promises and supports async/await syntax.

  • GET: fetch(url).then(res => res.json())
  • POST: fetch(url, { method: "POST", body: JSON.stringify(data) })
  • Async/Await: const data = await fetch(url).then(res => res.json())
  • Error Handling: if (!response.ok) throw new Error()
java
// Fetch API example
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    console.log(data);
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    throw error;
  }
}

// POST request
async function postData(url, data) {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data)
    });
    return await response.json();
  } catch (error) {
    console.error('POST error:', error);
    throw error;
  }
}

// With AbortController
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

fetch('https://api.example.com/data', {
  signal: controller.signal
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
  if (error.name === 'AbortError') {
    console.log('Request aborted');
  }
})
.finally(() => clearTimeout(timeoutId));
Coding Round
50. Create a promise

Create a promise using the Promise constructor with resolve and reject functions. Use it for async operations.

  • Constructor: new Promise((resolve, reject) => {})
  • resolve(): Fulfill the promise
  • reject(): Reject the promise
  • Then: promise.then(callback).catch(callback)
java
// Create a promise
function createPromise(shouldResolve) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldResolve) {
        resolve("Success!");
      } else {
        reject("Failed!");
      }
    }, 1000);
  });
}

// Using the promise
createPromise(true)
  .then(result => console.log("Resolved:", result))
  .catch(error => console.error("Rejected:", error));

// Promise with parameters
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, name: `User${id}` });
      } else {
        reject("Invalid ID");
      }
    }, 1000);
  });
}

fetchUser(1)
  .then(user => console.log(user))
  .catch(error => console.error(error));
Coding Round
51. Factorial

Calculate factorial using recursion or iteration. Factorial of n is the product of all positive integers less than or equal to n.

  • Recursive: n <= 1 ? 1 : n * factorial(n-1)
  • Iterative: Loop from 2 to n
  • Edge Cases: 0! = 1, negative numbers
  • Performance: Iterative is faster
java
// Factorial
function factorial(n) {
  if (n <= 1) return 1;
  return n * factorial(n - 1);
}
console.log(factorial(5)); // 120

// Iterative
function factorialIterative(n) {
  let result = 1;
  for (let i = 2; i <= n; i++) {
    result *= i;
  }
  return result;
}
Coding Round
52. Fibonacci

Calculate Fibonacci numbers using recursion, iteration, or memoization. The Fibonacci sequence starts with 0, 1, 1, 2, 3, 5, 8, ...

  • Recursive: n <= 1 ? n : fib(n-1) + fib(n-2)
  • Iterative: Loop with variables
  • Memoization: Cache results for performance
  • Time Complexity: O(2^n) recursive, O(n) iterative
java
// Fibonacci
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(8)); // 21

// Iterative
function fibonacciIterative(n) {
  let a = 0, b = 1;
  for (let i = 2; i <= n; i++) {
    [a, b] = [b, a + b];
  }
  return n ? b : a;
}
Coding Round
53. FizzBuzz

FizzBuzz prints numbers from 1 to n, but for multiples of 3 print "Fizz", for multiples of 5 print "Buzz", and for multiples of both print "FizzBuzz".

  • Logic: Check divisibility by 3 and 5
  • Order: Check 15 first, then 3, then 5
  • Output: Print each result
  • Use Cases: Common interview question
java
// FizzBuzz
function fizzBuzz(n) {
  for (let i = 1; i <= n; i++) {
    if (i % 15 === 0) console.log("FizzBuzz");
    else if (i % 3 === 0) console.log("Fizz");
    else if (i % 5 === 0) console.log("Buzz");
    else console.log(i);
  }
}
fizzBuzz(15);
Coding Round
54. Find missing number

Find the missing number in an array of consecutive integers. Use the formula n*(n+1)/2 - sum of array.

  • Formula: total = n * (n + 1) / 2
  • Missing: total - sum(arr)
  • XOR Method: XOR all numbers and indices
  • Edge Cases: Empty array, missing first/last
java
// Find missing number
function findMissing(arr) {
  const n = arr.length + 1;
  const total = n * (n + 1) / 2;
  const sum = arr.reduce((acc, x) => acc + x, 0);
  return total - sum;
}
console.log(findMissing([1, 2, 4, 5, 6])); // 3
Coding Round
55. Find duplicates

Find duplicate elements in an array using Set, Map, or filter. The Set method is the most efficient.

  • Set: new Set(arr.filter(x => arr.indexOf(x) !== arr.lastIndexOf(x)))
  • Map: Track frequency with Map
  • Filter: arr.filter((x, i) => arr.indexOf(x) !== i)
  • Time Complexity: O(n) with Set
java
// Find duplicates
function findDuplicates(arr) {
  const seen = new Set();
  const duplicates = new Set();
  for (const item of arr) {
    if (seen.has(item)) {
      duplicates.add(item);
    } else {
      seen.add(item);
    }
  }
  return [...duplicates];
}
console.log(findDuplicates([1, 2, 3, 2, 4, 3])); // [2, 3]
Coding Round
56. Sum of array

Calculate the sum of all elements in an array using reduce, for loop, or forEach method.

  • Reduce: arr.reduce((acc, x) => acc + x, 0)
  • For Loop: let sum = 0; for (let x of arr) sum += x;
  • forEach: arr.forEach(x => sum += x)
  • Edge Cases: Empty array returns 0
java
// Sum of array
function sumArray(arr) {
  return arr.reduce((acc, x) => acc + x, 0);
}
console.log(sumArray([1, 2, 3, 4, 5])); // 15
Coding Round
57. Average of array

Calculate the average of an array by dividing the sum by the array length. Handle empty arrays.

  • Method: sum / arr.length
  • Empty Array: Return 0 or null
  • Reduce: arr.reduce((a, x) => a + x, 0) / arr.length
  • Use Cases: Statistics, data analysis
java
// Average of array
function averageArray(arr) {
  return arr.reduce((acc, x) => acc + x, 0) / arr.length;
}
console.log(averageArray([1, 2, 3, 4, 5])); // 3
Coding Round
58. Sort array ascending

Sort an array in ascending order using the sort method with a comparator function. Numbers need a custom comparator.

  • Method: arr.sort((a, b) => a - b)
  • Spread: [...arr].sort((a, b) => a - b)
  • Strings: arr.sort() (works for strings)
  • Edge Cases: Negative numbers, decimals
java
// Sort array ascending
function sortAscending(arr) {
  return [...arr].sort((a, b) => a - b);
}
console.log(sortAscending([5, 2, 8, 1, 9])); // [1, 2, 5, 8, 9]
Coding Round
59. Sort array descending

Sort an array in descending order using the sort method with a comparator function that subtracts in reverse.

  • Method: arr.sort((a, b) => b - a)
  • Spread: [...arr].sort((a, b) => b - a)
  • Reverse: arr.sort((a, b) => a - b).reverse()
  • Edge Cases: Negative numbers, decimals
java
// Sort array descending
function sortDescending(arr) {
  return [...arr].sort((a, b) => b - a);
}
console.log(sortDescending([5, 2, 8, 1, 9])); // [9, 8, 5, 2, 1]
Coding Round
60. Flatten nested array

Flatten a nested array using recursion, reduce, or the flat() method. Handle multiple levels of nesting.

  • flat(): arr.flat(Infinity)
  • Recursive: arr.reduce((acc, x) => acc.concat(Array.isArray(x) ? flatten(x) : x), [])
  • Stack: Use a stack for iterative flattening
  • Depth: Specify depth or flatten completely
java
// Flatten nested array
function flattenArray(arr) {
  return arr.reduce((acc, val) => 
    Array.isArray(val) ? acc.concat(flattenArray(val)) : acc.concat(val), 
    []
  );
}
console.log(flattenArray([1, [2, [3, 4], 5], 6])); // [1, 2, 3, 4, 5, 6]

// ES2019 flat
const flat = arr.flat(Infinity);
Coding Round
61. Chunk array

Split an array into chunks of a specified size. Use slice and push in a loop for efficient chunking.

  • Method: Loop with arr.slice(i, i + size)
  • Reduce: arr.reduce((acc, x, i) => i % size === 0 ? [...acc, [x]] : acc.map((chunk, j) => j === acc.length - 1 ? [...chunk, x] : chunk), [])
  • Use Cases: Pagination, batch processing
  • Edge Cases: Empty array, size larger than array
java
// Chunk array
function chunkArray(arr, size) {
  const result = [];
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size));
  }
  return result;
}
console.log(chunkArray([1, 2, 3, 4, 5, 6], 2)); // [[1,2], [3,4], [5,6]]
Coding Round
63. Quick sort

Quick sort is a divide-and-conquer algorithm that picks a pivot and partitions the array around it. O(n log n) average time.

  • Algorithm: Choose pivot, partition, recursively sort
  • Pivot: First element, last element, random
  • In-place: Can be implemented in-place
  • Time Complexity: O(n log n) average, O(n²) worst
java
// Quick sort
function quickSort(arr) {
  if (arr.length <= 1) return arr;
  const pivot = arr[0];
  const left = arr.slice(1).filter(x => x < pivot);
  const right = arr.slice(1).filter(x => x >= pivot);
  return [...quickSort(left), pivot, ...quickSort(right)];
}
console.log(quickSort([5, 3, 8, 4, 2, 7, 1, 6]));
Coding Round
64. Merge sort

Merge sort is a divide-and-conquer algorithm that divides the array into halves, recursively sorts them, and merges the results. O(n log n) time.

  • Algorithm: Divide, recursively sort, merge
  • Merge Function: Merge two sorted arrays
  • Stable: Maintains relative order of equal elements
  • Time Complexity: O(n log n) guaranteed
java
// Merge sort
function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  const mid = Math.floor(arr.length / 2);
  const left = mergeSort(arr.slice(0, mid));
  const right = mergeSort(arr.slice(mid));
  return merge(left, right);
}

function merge(left, right) {
  const result = [];
  let i = 0, j = 0;
  while (i < left.length && j < right.length) {
    if (left[i] <= right[j]) result.push(left[i++]);
    else result.push(right[j++]);
  }
  return [...result, ...left.slice(i), ...right.slice(j)];
}
Coding Round
65. Bubble sort

Bubble sort repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. O(n²) time.

  • Algorithm: Compare adjacent, swap if needed
  • Optimization: Stop if no swaps in a pass
  • Time Complexity: O(n²) worst case
  • Use Cases: Educational, small datasets
java
// Bubble sort
function bubbleSort(arr) {
  const sorted = [...arr];
  for (let i = 0; i < sorted.length - 1; i++) {
    for (let j = 0; j < sorted.length - 1 - i; j++) {
      if (sorted[j] > sorted[j + 1]) {
        [sorted[j], sorted[j + 1]] = [sorted[j + 1], sorted[j]];
      }
    }
  }
  return sorted;
}
Coding Round
66. Intersection of arrays

Find the intersection of two arrays (elements present in both). Use filter with includes or Set for efficiency.

  • Filter: arr1.filter(x => arr2.includes(x))
  • Set: [...new Set(arr1)].filter(x => new Set(arr2).has(x))
  • Time Complexity: O(n²) with includes, O(n) with Set
  • Unique: Handle duplicates
java
// Intersection of arrays
function intersection(arr1, arr2) {
  return arr1.filter(x => arr2.includes(x));
}
console.log(intersection([1, 2, 3, 4], [3, 4, 5, 6])); // [3, 4]
Coding Round
67. Union of arrays

Find the union of two arrays (all elements from both, no duplicates). Use Set for efficient unique union.

  • Set: [...new Set([...arr1, ...arr2])]
  • Concat: arr1.concat(arr2.filter(x => !arr1.includes(x)))
  • Time Complexity: O(n) with Set
  • Order: Maintains first occurrence order
java
// Union of arrays
function union(arr1, arr2) {
  return [...new Set([...arr1, ...arr2])];
}
console.log(union([1, 2, 3], [3, 4, 5])); // [1, 2, 3, 4, 5]
Coding Round
68. Difference of arrays

Find the difference of two arrays (elements in the first array not in the second). Use filter with includes or Set.

  • Filter: arr1.filter(x => !arr2.includes(x))
  • Set: arr1.filter(x => !new Set(arr2).has(x))
  • Symmetric Difference: [...diff1, ...diff2]
  • Time Complexity: O(n²) with includes, O(n) with Set
java
// Difference of arrays
function difference(arr1, arr2) {
  return arr1.filter(x => !arr2.includes(x));
}
console.log(difference([1, 2, 3, 4], [3, 4, 5, 6])); // [1, 2]
Coding Round
69. Group by property

Group an array of objects by a property using reduce. Create a new object with groups as keys.

  • Method: arr.reduce((acc, item) => { acc[item.key] = [...(acc[item.key] || []), item]; return acc; }, {})
  • Use Cases: Data aggregation, categorization
  • Group By Multiple: Use composite keys
  • Performance: O(n) time complexity
java
// Group by property
function groupBy(arr, key) {
  return arr.reduce((acc, item) => {
    const group = item[key];
    if (!acc[group]) acc[group] = [];
    acc[group].push(item);
    return acc;
  }, {});
}
const data = [{type: 'fruit', name: 'apple'}, {type: 'fruit', name: 'banana'}, {type: 'veg', name: 'carrot'}];
console.log(groupBy(data, 'type'));
Coding Round
70. Deep clone object

Deep clone an object to create a completely independent copy. Use JSON methods or recursive cloning for complex objects.

  • JSON: JSON.parse(JSON.stringify(obj))
  • Recursive: Handle nested objects and arrays
  • Limitations: Functions, Date, RegExp, circular references
  • Lodash: _.cloneDeep(obj)
java
// Deep clone object
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}
const original = { a: 1, b: { c: 2 } };
const cloned = deepClone(original);
cloned.b.c = 3;
console.log(original.b.c); // 2

// With circular reference handling
function deepCloneAdvanced(obj, seen = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (seen.has(obj)) return seen.get(obj);
  const clone = Array.isArray(obj) ? [] : {};
  seen.set(obj, clone);
  for (const key in obj) {
    clone[key] = deepCloneAdvanced(obj[key], seen);
  }
  return clone;
}
Coding Round
71. Immutable update

Perform immutable updates on nested objects by creating new copies at each level. Use spread operator or libraries like Immer.

  • Spread: { ...obj, nested: { ...obj.nested, prop: newValue } }
  • Path: Update by path string
  • Immer: produce(state, draft => { draft.nested.prop = newValue })
  • Use Cases: State management, React state
java
// Immutable update
function updateImmutable(obj, path, value) {
  const keys = path.split('.');
  if (keys.length === 1) {
    return { ...obj, [keys[0]]: value };
  }
  const [first, ...rest] = keys;
  return {
    ...obj,
    [first]: updateImmutable(obj[first] || {}, rest.join('.'), value)
  };
}
const state = { user: { name: 'Alice', age: 25 } };
const newState = updateImmutable(state, 'user.age', 26);
console.log(state.user.age); // 25
console.log(newState.user.age); // 26
Coding Round
72. Pipe function

Pipe is a function composition technique that passes the result of one function to the next. It reads left to right.

  • Method: pipe(fn1, fn2, fn3)(value)
  • Implementation: fns.reduce((acc, fn) => fn(acc), value)
  • Use Cases: Data transformation, functional programming
  • Reverse: Compose for right-to-left
java
// Pipe function
function pipe(...fns) {
  return (value) => fns.reduce((acc, fn) => fn(acc), value);
}

const double = x => x * 2;
const addTen = x => x + 10;
const square = x => x * x;

const process = pipe(double, addTen, square);
console.log(process(5)); // (5*2+10)^2 = 400
Coding Round
73. Compose function

Compose is a function composition technique that passes the result of one function to the next, but reads from right to left.

  • Method: compose(fn3, fn2, fn1)(value)
  • Implementation: fns.reduceRight((acc, fn) => fn(acc), value)
  • Use Cases: Data transformation, functional programming
  • Order: Functions are applied from right to left
java
// Compose function
function compose(...fns) {
  return (value) => fns.reduceRight((acc, fn) => fn(acc), value);
}

const process2 = compose(square, addTen, double);
console.log(process2(5)); // (5*2+10)^2 = 400
Coding Round
74. Memoization

Memoization is an optimization technique that caches function results based on arguments to avoid expensive recalculations.

  • Method: Cache results in a Map or object
  • Key: Serialize arguments as key
  • Use Cases: Expensive functions, recursive algorithms
  • Trade-off: Memory for speed
java
// Memoization
function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const factorialMemo = memoize(function(n) {
  if (n <= 1) return 1;
  return n * factorialMemo(n - 1);
});
console.log(factorialMemo(5)); // 120
Coding Round
75. Once function

The once function ensures a function is called only once, regardless of how many times it's invoked. Subsequent calls return the cached result.

  • Method: Track if function has been called
  • Implementation: Use a closure with a flag
  • Use Cases: Initialization, setup operations
  • Thread Safety: Not needed in single-threaded JavaScript
java
// Once function
function once(fn) {
  let called = false;
  let result;
  return function(...args) {
    if (!called) {
      called = true;
      result = fn(...args);
    }
    return result;
  };
}

const initialize = once(() => {
  console.log("Initialized");
  return { id: 1, name: "App" };
});

initialize(); // Prints "Initialized"
initialize(); // Returns cached result
Coding Round
76. Debounce with leading edge

Debounce with leading edge executes the function immediately on the first call, then waits for the delay period before allowing another execution.

  • Method: Track last call time
  • Implementation: Immediate execution, then cooldown
  • Use Cases: Save actions, API calls
  • Difference: Leading vs trailing edge
java
// Debounce with leading edge
function debounceLeading(func, delay) {
  let timeoutId;
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall < delay) {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        lastCall = Date.now();
        func.apply(this, args);
      }, delay);
    } else {
      lastCall = now;
      func.apply(this, args);
    }
  };
}
Coding Round
77. Throttle with leading edge

Throttle with leading edge executes the function immediately on the first call, then at most once per specified time period.

  • Method: Track last call time
  • Implementation: Immediate execution, then limit
  • Use Cases: Scroll events, resize events
  • Difference: Leading vs trailing edge
java
// Throttle with leading edge
function throttleLeading(func, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      func.apply(this, args);
    }
  };
}
Coding Round
78. Deep equal

Deep equal checks if two values are deeply equal by recursively comparing nested objects and arrays.

  • Method: Recursive comparison
  • Base Cases: Primitive values, null, undefined
  • Objects: Compare keys and values recursively
  • Circular References: Handle with Set
java
// Deep equal
function deepEqual(obj1, obj2) {
  if (obj1 === obj2) return true;
  if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || 
      obj1 === null || obj2 === null) return false;
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) return false;
  for (const key of keys1) {
    if (!keys2.includes(key)) return false;
    if (!deepEqual(obj1[key], obj2[key])) return false;
  }
  return true;
}
Coding Round
79. Observable pattern

The Observable pattern allows objects to subscribe to changes and get notified when the observable state changes. It's a popular pattern in reactive programming.

  • Observable: Maintains a list of subscribers
  • Subscriber: Receives notifications
  • Methods: subscribe, notify, unsubscribe
  • Use Cases: Event handling, state management
java
// Observable pattern
class Observable {
  constructor() {
    this.subscribers = [];
  }
  
  subscribe(callback) {
    this.subscribers.push(callback);
    return () => {
      this.subscribers = this.subscribers.filter(cb => cb !== callback);
    };
  }
  
  notify(data) {
    this.subscribers.forEach(cb => cb(data));
  }
}

const observable = new Observable();
const unsubscribe = observable.subscribe(data => console.log('Received:', data));
observable.notify('Hello'); // Received: Hello
unsubscribe();
observable.notify('World'); // Nothing happens
Coding Round
80. Singleton pattern

The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. Useful for configuration, logging, and caching.

  • Implementation: Store instance in static property
  • Lazy Initialization: Create instance only when needed
  • Module Pattern: ES modules are singletons
  • Use Cases: Logger, database connection, config
java
// Singleton pattern
class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = this;
    this.data = {};
    return this;
  }
  
  set(key, value) {
    this.data[key] = value;
  }
  
  get(key) {
    return this.data[key];
  }
}

const s1 = new Singleton();
const s2 = new Singleton();
s1.set('name', 'Alice');
console.log(s2.get('name')); // Alice
console.log(s1 === s2); // true
Coding Round
81. Factory pattern

The Factory pattern provides a way to create objects without specifying the exact class. It encapsulates object creation logic and provides flexibility.

  • Method: Factory function or class
  • Benefits: Decouples creation from usage
  • Use Cases: Creating different types of objects
  • Parameterized: Pass parameters for customization
java
// Factory pattern
class UserFactory {
  createUser(type, name) {
    switch(type) {
      case 'admin':
        return new Admin(name);
      case 'guest':
        return new Guest(name);
      default:
        return new User(name);
    }
  }
}

class User {
  constructor(name) { this.name = name; this.type = 'user'; }
}
class Admin extends User {
  constructor(name) { super(name); this.type = 'admin'; }
}
class Guest extends User {
  constructor(name) { super(name); this.type = 'guest'; }
}

const factory = new UserFactory();
const admin = factory.createUser('admin', 'Alice');
console.log(admin.type); // admin
Coding Round
82. Strategy pattern

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to vary independently.

  • Context: Uses a strategy object
  • Strategy: Interface for algorithms
  • Benefits: Open/closed principle, runtime switching
  • Use Cases: Payment methods, sorting algorithms
java
// Strategy pattern
class PaymentStrategy {
  pay(amount) { throw new Error('Must implement pay'); }
}

class CreditCardStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid ${amount} with Credit Card`);
  }
}

class PayPalStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid ${amount} with PayPal`);
  }
}

class CryptoStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid ${amount} with Crypto`);
  }
}

class PaymentContext {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  executePayment(amount) {
    this.strategy.pay(amount);
  }
}

const context = new PaymentContext(new CreditCardStrategy());
context.executePayment(100);
context.setStrategy(new PayPalStrategy());
context.executePayment(50);
Coding Round
83. Observer pattern

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified automatically.

  • Subject: Maintains observers
  • Observer: Receives updates
  • Benefits: Loose coupling, event-driven architecture
  • Use Cases: Event handling, pub/sub systems
java
// Observer pattern
class Subject {
  constructor() {
    this.observers = [];
  }
  
  attach(observer) {
    this.observers.push(observer);
  }
  
  detach(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }
  
  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class ConcreteObserver {
  constructor(name) {
    this.name = name;
  }
  
  update(data) {
    console.log(`${this.name} received: ${data}`);
  }
}

const subject = new Subject();
const observer1 = new ConcreteObserver('Observer1');
const observer2 = new ConcreteObserver('Observer2');
subject.attach(observer1);
subject.attach(observer2);
subject.notify('Hello World');
Coding Round
84. Decorator pattern

The Decorator pattern allows behavior to be added to individual objects dynamically without affecting other objects from the same class.

  • Component: Base interface
  • Decorator: Wraps component and adds behavior
  • Benefits: Flexible extension, open/closed principle
  • Use Cases: Logging, authentication, caching
java
// Decorator pattern
class Coffee {
  cost() { return 5; }
  description() { return 'Coffee'; }
}

class MilkDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
  cost() { return this.coffee.cost() + 2; }
  description() { return this.coffee.description() + ', Milk'; }
}

class SugarDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }
  cost() { return this.coffee.cost() + 1; }
  description() { return this.coffee.description() + ', Sugar'; }
}

let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(coffee.description()); // Coffee, Milk, Sugar
console.log(coffee.cost()); // 8
Coding Round
85. Command pattern

The Command pattern encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing, logging, and undo operations.

  • Command: Encapsulates request
  • Invoker: Executes commands
  • Receiver: Performs the actual work
  • Benefits: Undo/redo, queuing, logging
java
// Command pattern
class Command {
  execute() {}
  undo() {}
}

class AddCommand extends Command {
  constructor(receiver, value) {
    super();
    this.receiver = receiver;
    this.value = value;
  }
  execute() {
    this.receiver.add(this.value);
  }
  undo() {
    this.receiver.subtract(this.value);
  }
}

class Calculator {
  constructor() {
    this.value = 0;
  }
  add(n) { this.value += n; }
  subtract(n) { this.value -= n; }
  getValue() { return this.value; }
}

const calc = new Calculator();
const addCommand = new AddCommand(calc, 5);
addCommand.execute();
console.log(calc.getValue()); // 5
addCommand.undo();
console.log(calc.getValue()); // 0
Coding Round
86. Memento pattern

The Memento pattern captures and externalizes an object's internal state so that the object can be restored to that state later without violating encapsulation.

  • Originator: Creates and restores mementos
  • Memento: Stores internal state
  • Caretaker: Manages mementos
  • Benefits: State restoration, undo/redo
java
// Memento pattern
class Memento {
  constructor(state) {
    this.state = state;
  }
  getState() { return this.state; }
}

class Originator {
  constructor() {
    this.state = '';
  }
  setState(state) {
    this.state = state;
  }
  getState() {
    return this.state;
  }
  saveState() {
    return new Memento(this.state);
  }
  restoreState(memento) {
    this.state = memento.getState();
  }
}

class Caretaker {
  constructor() {
    this.mementos = [];
  }
  addMemento(memento) {
    this.mementos.push(memento);
  }
  getMemento(index) {
    return this.mementos[index];
  }
}
Coding Round
87. Mediator pattern

The Mediator pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly.

  • Mediator: Encapsulates communication
  • Colleague: Communicates through mediator
  • Benefits: Loose coupling, centralized control
  • Use Cases: Chat systems, UI components
java
// Mediator pattern
class Mediator {
  constructor() {
    this.colleagues = [];
  }
  register(colleague) {
    this.colleagues.push(colleague);
  }
  send(message, sender) {
    this.colleagues.forEach(colleague => {
      if (colleague !== sender) {
        colleague.receive(message);
      }
    });
  }
}

class Colleague {
  constructor(mediator, name) {
    this.mediator = mediator;
    this.name = name;
    this.mediator.register(this);
  }
  send(message) {
    this.mediator.send(message, this);
  }
  receive(message) {
    console.log(`${this.name} received: ${message}`);
  }
}

const mediator = new Mediator();
const alice = new Colleague(mediator, 'Alice');
const bob = new Colleague(mediator, 'Bob');
alice.send('Hello Bob!');
Coding Round
88. Chain of Responsibility

The Chain of Responsibility pattern passes a request along a chain of handlers until one of them handles it. Each handler decides whether to process the request or pass it on.

  • Handler: Processes or forwards request
  • Chain: Linked list of handlers
  • Benefits: Decoupling, dynamic configuration
  • Use Cases: Logging, authentication, middleware
java
// Chain of Responsibility
class Handler {
  constructor() {
    this.next = null;
  }
  setNext(handler) {
    this.next = handler;
    return handler;
  }
  handle(request) {
    if (this.next) {
      this.next.handle(request);
    }
  }
}

class AuthHandler extends Handler {
  handle(request) {
    if (request.token) {
      console.log('Authentication passed');
      super.handle(request);
    } else {
      console.log('Authentication failed');
    }
  }
}

class LoggerHandler extends Handler {
  handle(request) {
    console.log(`Logging request: ${request.url}`);
    super.handle(request);
  }
}

const auth = new AuthHandler();
const logger = new LoggerHandler();
auth.setNext(logger);
auth.handle({ token: 'valid', url: '/api' });
Coding Round
89. State pattern

The State pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.

  • Context: Maintains state
  • State: Defines behavior for each state
  • Benefits: Clean state management, avoids conditionals
  • Use Cases: State machines, workflow engines
java
// State pattern
class State {
  handle() {}
}

class ReadyState extends State {
  handle() {
    console.log('Ready: Waiting for input');
  }
}

class ProcessingState extends State {
  handle() {
    console.log('Processing: Working on task');
  }
}

class CompletedState extends State {
  handle() {
    console.log('Completed: Task finished');
  }
}

class Context {
  constructor() {
    this.state = new ReadyState();
  }
  setState(state) {
    this.state = state;
  }
  request() {
    this.state.handle();
  }
}

const context2 = new Context();
context2.request(); // Ready: Waiting for input
context2.setState(new ProcessingState());
context2.request(); // Processing: Working on task
context2.setState(new CompletedState());
context2.request(); // Completed: Task finished
Coding Round
90. Proxy pattern

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. It can add behavior like caching, logging, or access control.

  • Subject: Real object
  • Proxy: Controls access to subject
  • Benefits: Access control, lazy loading, logging
  • Use Cases: Virtual proxies, protection proxies
java
// Proxy pattern
class RealSubject {
  request() {
    console.log('RealSubject: Handling request');
  }
}

class Proxy {
  constructor(realSubject) {
    this.realSubject = realSubject;
  }
  request() {
    if (this.checkAccess()) {
      this.realSubject.request();
      this.logAccess();
    }
  }
  checkAccess() {
    console.log('Proxy: Checking access');
    return true;
  }
  logAccess() {
    console.log('Proxy: Logging access');
  }
}

const real = new RealSubject();
const proxy = new Proxy(real);
proxy.request();
Coding Round
91. Flyweight pattern

The Flyweight pattern minimizes memory usage by sharing as much data as possible with similar objects. It's useful for large numbers of similar objects.

  • Flyweight: Shared object
  • Factory: Manages flyweights
  • Benefits: Memory optimization, performance
  • Use Cases: Text rendering, caching
java
// Flyweight pattern
class Flyweight {
  constructor(sharedState) {
    this.sharedState = sharedState;
  }
  operation(uniqueState) {
    console.log(`Shared: ${this.sharedState}, Unique: ${uniqueState}`);
  }
}

class FlyweightFactory {
  constructor() {
    this.flyweights = {};
  }
  getFlyweight(sharedState) {
    if (!this.flyweights[sharedState]) {
      this.flyweights[sharedState] = new Flyweight(sharedState);
    }
    return this.flyweights[sharedState];
  }
}
Coding Round
92. Bridge pattern

The Bridge pattern decouples an abstraction from its implementation so that the two can vary independently. It's useful for separating interface from implementation.

  • Abstraction: High-level interface
  • Implementation: Low-level operations
  • Benefits: Separation of concerns, flexibility
  • Use Cases: Cross-platform applications
java
// Bridge pattern
class Abstraction {
  constructor(implementation) {
    this.implementation = implementation;
  }
  operation() {
    this.implementation.operation();
  }
}

class RefinedAbstraction extends Abstraction {
  operation() {
    console.log('RefinedAbstraction: Additional logic');
    this.implementation.operation();
  }
}

class ConcreteImplementationA {
  operation() {
    console.log('ConcreteImplementationA: Operation');
  }
}

class ConcreteImplementationB {
  operation() {
    console.log('ConcreteImplementationB: Operation');
  }
}
Coding Round
93. Adapter pattern

The Adapter pattern converts the interface of a class into another interface that clients expect. It allows incompatible interfaces to work together.

  • Target: Expected interface
  • Adaptee: Existing interface
  • Adapter: Bridges target and adaptee
  • Benefits: Reusability, legacy integration
java
// Adapter pattern
class Target {
  request() {
    console.log('Target: Request');
  }
}

class Adaptee {
  specificRequest() {
    console.log('Adaptee: Specific Request');
  }
}

class Adapter extends Target {
  constructor(adaptee) {
    super();
    this.adaptee = adaptee;
  }
  request() {
    this.adaptee.specificRequest();
  }
}

const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
adapter.request();
Coding Round
94. Facade pattern

The Facade pattern provides a simplified interface to a complex subsystem. It hides the complexity and makes the subsystem easier to use.

  • Facade: Simplified interface
  • Subsystem: Complex components
  • Benefits: Simplified interface, decoupling
  • Use Cases: Library APIs, complex systems
java
// Facade pattern
class SubsystemA {
  operationA() { console.log('SubsystemA: Operation'); }
}

class SubsystemB {
  operationB() { console.log('SubsystemB: Operation'); }
}

class Facade {
  constructor() {
    this.subsystemA = new SubsystemA();
    this.subsystemB = new SubsystemB();
  }
  operation() {
    this.subsystemA.operationA();
    this.subsystemB.operationB();
    console.log('Facade: Complex operation');
  }
}

const facade = new Facade();
facade.operation();
Coding Round
95. Composite pattern

The Composite pattern composes objects into tree structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions uniformly.

  • Component: Interface for all objects
  • Leaf: Individual object
  • Composite: Container of components
  • Benefits: Uniform interface, tree structures
java
// Composite pattern
class Component {
  operation() {}
}

class Leaf extends Component {
  operation() {
    console.log('Leaf: Operation');
  }
}

class Composite extends Component {
  constructor() {
    super();
    this.children = [];
  }
  add(component) {
    this.children.push(component);
  }
  remove(component) {
    this.children = this.children.filter(c => c !== component);
  }
  operation() {
    console.log('Composite: Operation');
    this.children.forEach(child => child.operation());
  }
}
Coding Round
96. Visitor pattern

The Visitor pattern lets you add further operations to objects without having to modify them. It separates an algorithm from the object structure.

  • Visitor: Defines operations
  • Element: Accepts visitors
  • Benefits: Adding new operations without modifying elements
  • Use Cases: Compilers, AST traversal
java
// Visitor pattern
class Visitor {
  visitElementA(element) {}
  visitElementB(element) {}
}

class Element {
  accept(visitor) {}
}

class ElementA extends Element {
  accept(visitor) {
    visitor.visitElementA(this);
  }
}

class ElementB extends Element {
  accept(visitor) {
    visitor.visitElementB(this);
  }
}

class ConcreteVisitor extends Visitor {
  visitElementA(element) {
    console.log('Visiting ElementA');
  }
  visitElementB(element) {
    console.log('Visiting ElementB');
  }
}
Coding Round
97. Iterator pattern

The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

  • Iterator: Traverses collection
  • Aggregate: Creates iterator
  • Benefits: Uniform traversal, multiple iterators
  • Use Cases: Collection traversal, custom data structures
java
// Iterator pattern
class Iterator {
  constructor(collection) {
    this.collection = collection;
    this.index = 0;
  }
  next() {
    return this.collection[this.index++];
  }
  hasNext() {
    return this.index < this.collection.length;
  }
}

class CustomCollection {
  constructor() {
    this.items = [];
  }
  add(item) {
    this.items.push(item);
  }
  getIterator() {
    return new Iterator(this.items);
  }
}

const collection = new CustomCollection();
collection.add('A');
collection.add('B');
collection.add('C');
const iterator = collection.getIterator();
while (iterator.hasNext()) {
  console.log(iterator.next());
}
Coding Round
98. Template Method pattern

The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It lets subclasses redefine certain steps without changing the algorithm's structure.

  • AbstractClass: Defines template method
  • ConcreteClass: Implements abstract steps
  • Benefits: Code reuse, consistent algorithm structure
  • Use Cases: Frameworks, algorithms with customizable steps
java
// Template Method pattern
class AbstractClass {
  templateMethod() {
    this.step1();
    this.step2();
    this.step3();
  }
  step1() { console.log('Step 1'); }
  step2() { console.log('Step 2'); }
  step3() { console.log('Step 3'); }
}

class ConcreteClass extends AbstractClass {
  step2() {
    console.log('Concrete Step 2');
  }
}

const concrete = new ConcreteClass();
concrete.templateMethod();
Coding Round
99. Builder pattern

The Builder pattern constructs complex objects step by step. It separates the construction of a complex object from its representation.

  • Builder: Constructs parts
  • Director: Orchestrates construction
  • Product: The constructed object
  • Benefits: Step-by-step construction, reusable builder
java
// Builder pattern
class Product {
  constructor() {
    this.parts = [];
  }
  add(part) { this.parts.push(part); }
  listParts() { console.log(this.parts.join(', ')); }
}

class Builder {
  reset() {}
  buildStepA() {}
  buildStepB() {}
  getResult() {}
}

class ConcreteBuilder extends Builder {
  constructor() {
    super();
    this.product = new Product();
  }
  reset() {
    this.product = new Product();
  }
  buildStepA() {
    this.product.add('Part A');
  }
  buildStepB() {
    this.product.add('Part B');
  }
  getResult() {
    return this.product;
  }
}

class Director {
  constructor(builder) {
    this.builder = builder;
  }
  buildMinimal() {
    this.builder.buildStepA();
  }
  buildFull() {
    this.builder.buildStepA();
    this.builder.buildStepB();
  }
}
Coding Round
100. Prototype pattern

The Prototype pattern creates new objects by cloning an existing object, rather than instantiating new ones. It's useful when creating objects is expensive.

  • Prototype: Cloneable object
  • Clone: Creates a copy
  • Benefits: Performance, avoids constructors
  • Use Cases: Complex objects, expensive creation
java
// Prototype pattern
class Prototype {
  clone() {
    return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  }
  deepClone() {
    return JSON.parse(JSON.stringify(this));
  }
}

class ConcretePrototype extends Prototype {
  constructor(name) {
    super();
    this.name = name;
    this.nested = { value: 42 };
  }
}

const original2 = new ConcretePrototype('Original');
const copy = original2.clone();
copy.name = 'Copy';
copy.nested.value = 99;
console.log(original2.name); // Original
console.log(original2.nested.value); // 99 (shallow copy)

const deepCopy = original2.deepClone();
deepCopy.nested.value = 100;
console.log(original2.nested.value); // 99 (deep copy)