Skip to content

Instantly share code, notes, and snippets.

@carefree-ladka
Created June 1, 2026 10:27
Show Gist options
  • Select an option

  • Save carefree-ladka/54418fe9d9dcb59627ad6ce357d2a891 to your computer and use it in GitHub Desktop.

Select an option

Save carefree-ladka/54418fe9d9dcb59627ad6ce357d2a891 to your computer and use it in GitHub Desktop.
JavaScript Interview Notes

JavaScript Interview Notes

A comprehensive reference covering core JS concepts, async patterns, array methods, and output-based questions.


Table of Contents


Core Concepts

1. == vs === Operators

== (loose equality) performs type coercion before comparing. === (strict equality) compares both value and type — no coercion.

0 == false        // true  — coercion: false → 0
0 === false       // false — different types
1 == "1"          // true  — string "1" → number 1
1 === "1"         // false
null == undefined // true  — special case
null === undefined// false
'0' == false      // true  — '0' → 0 → false
NaN == NaN        // false — NaN is never equal to itself
[] == []          // false — different object references
{} == {}          // false — different object references

2. Hoisting

Hoisting is JavaScript's default behavior where variable and function declarations are moved to the top of their scope before code executes.

console.log(message); // undefined (var is hoisted)
var message = "The variable has been hoisted";

// Function declarations are fully hoisted:
message("Good morning"); // works fine

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

3. Scope

Scope defines where a variable can be accessed in JavaScript. There are three types:

1. Global Scope — accessible anywhere in the code.

let city = "Delhi";

function test() {
  console.log(city); // Delhi
}

2. Function Scope — accessible only inside the function.

function demo() {
  var age = 22;
  console.log(age); // 22
}
console.log(age); // ReferenceError

3. Block Scope — accessible only within curly braces {}.

{
  let a = 10;
  const b = 20;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

4. Undefined Property

If you access a property that does not exist on an object, JavaScript returns undefined.


5. Null Value

null means intentionally assigned empty value. It is explicitly set by the developer to indicate "no value."


6. Null vs Undefined

null undefined
Intentionally empty value Variable declared but not assigned
Type: "object" (JS quirk) Type: "undefined"
null == undefinedtrue null === undefinedfalse

7. Window vs Document

Window

  • Root-level element in any web page.
  • Available implicitly by default.
  • Has methods like alert(), confirm() and properties like document, location.

Document

  • Direct child of the window object (also known as the DOM).
  • Accessible via window.document or just document.
  • Provides methods like getElementById, createElement, etc.

8. isNaN

isNaN() checks whether a value is Not a Number. It coerces the value to a number first.

console.log(isNaN(10));        // false
console.log(isNaN("10"));      // false — "10" → 10
console.log(isNaN("hello"));   // true
console.log(isNaN(undefined)); // true — undefined → NaN

9. Undeclared vs Undefined Variables

Undeclared Undefined
Does not exist in the program Declared but not assigned a value
Causes a runtime ReferenceError Returns undefined
var a;
a; // undefined

b; // ReferenceError: b is not defined

10. Global Variables

Global variables are accessible throughout the entire codebase without any scope restriction. Avoid unnecessary globals — they use extra memory and can be accidentally overwritten.


11. Compiled vs Interpreted

JavaScript is both interpreted and compiled — modern JS engines use JIT (Just-In-Time) Compilation.

Parse → Compile → Execute

The JS engine (e.g. V8) parses the code, compiles it to machine code, then executes it — making execution fast.


12. Case Sensitivity

JavaScript is case-sensitive. age and Age are treated as different identifiers.

let age = 20;
let Age = 30;

console.log(age); // 20
console.log(Age); // 30

13. Java vs JavaScript

Despite the name, Java and JavaScript are completely unrelated languages. JavaScript was originally named "Mocha," then "LiveScript," and was renamed to JavaScript as a marketing decision when Java was popular. They differ in syntax, runtime, type system, and use cases.


14. Single Threaded

JavaScript is called single-threaded because it executes one task at a time using a single call stack.

console.log(1);
console.log(2);
console.log(3);
// Output: 1, 2, 3 — always in order

15. ECMAScript

ECMAScript is the standard specification that defines how JavaScript should work.

  • ECMAScript → Rules / Specification
  • JavaScript → Implementation of those rules

ES6 / ES2015 was the most impactful update, introducing: let & const, arrow functions, classes, promises, and template literals.


16. Primitive vs Reference Types

Primitive types store actual values and are immutable and copied by value.

Primitive Types
String
Number
Boolean
Undefined
Null
BigInt
Symbol

Reference types store the memory reference/address of the value and are copied by reference.

Reference Types
Object
Array
Function
let user1 = { name: "Dimitri" };
let user2 = user1;

user2.name = "Alex";
console.log(user1.name); // "Alex" — both point to same memory

17. Detecting Primitive vs Non-Primitive

Use typeof to detect the type.

// Primitives
typeof "Hello"     // "string"
typeof 10          // "number"
typeof true        // "boolean"
typeof undefined   // "undefined"
typeof 10n         // "bigint"
typeof Symbol()    // "symbol"

// Non-Primitives
typeof {}          // "object"
typeof []          // "object"
typeof function(){}// "function"

// ⚠️ Famous JS quirk:
typeof null        // "object" — this is a known bug in JavaScript

18. isNaN vs Number.isNaN

isNaN() Number.isNaN()
Performs type coercion first No type coercion
isNaN("hello")true Number.isNaN("hello")false
isNaN(null)false (null→0) Number.isNaN(null)false
// isNaN coerces:
isNaN(" ")         // false — " " → 0
isNaN(null)        // false — null → 0
isNaN(undefined)   // true  — undefined → NaN

// Number.isNaN is strict:
Number.isNaN(NaN)      // true
Number.isNaN("hello")  // false — no coercion, not actually NaN
Number.isNaN(undefined)// false

19. Lexical Scope

Lexical scope means a function can access variables from its parent scope based on where it is defined (not where it is called).

let name = "Dimitri";

function outer() {
  function inner() {
    console.log(name); // "Dimitri" — accesses parent scope
  }
  inner();
}

outer();

Functions

20. First Class Functions

JavaScript functions are first-class — they can be stored, passed, and returned like any other value.

// 1. Function as a variable
const greet = function () {
  console.log("Hello");
};

// 2. Function as argument
function execute(fn) {
  fn();
}
execute(greet);

// 3. Function returning a function
function outer() {
  return function () {
    console.log("Inner");
  };
}
const fn = outer();
fn();

21. First Order Functions

A first-order function neither accepts nor returns another function.

function add(a, b) {
  return a + b;
}
console.log(add(2, 3)); // 5

22. Higher Order Functions

A higher-order function accepts another function as an argument or returns a function.

// Accepts a function
function execute(fn) {
  fn();
}

// Returns a function
function outer() {
  return function () {
    console.log("Inner");
  };
}

Built-in examples: map(), filter(), reduce(), forEach().


23. Unary Functions

A unary function accepts only one argument.

function square(num) {
  return num * num;
}
Argument Count Name
1 Unary
2 Binary
3 Ternary

24. Currying

Currying converts a function with multiple arguments into nested functions that each take one argument at a time.

function add(a) {
  return function (b) {
    return function (c) {
      return a + b + c;
    };
  };
}

console.log(add(1)(2)(3)); // 6

25. Pure Functions

A pure function always returns the same output for the same input and causes no side effects.

// Pure
function add(a, b) {
  return a + b;
}

// Impure — modifies external state
let count = 0;
function increase() {
  count++;
  return count;
}

Benefits: Predictable output, easier testing, simpler debugging, and reusability.


26. IIFE

An IIFE (Immediately Invoked Function Expression) executes immediately after being created. Commonly used to create private variables and avoid polluting the global scope.

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

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

The first () makes it an expression; the last () immediately calls it.


27. Memoization

Memoization stores function results so repeated inputs return cached results instead of recalculating.

Benefits: Improved performance, avoids redundant calculations.
Used in: Recursion, dynamic programming, React (React.memo, useMemo).


28. Closures

A closure is created when a function remembers variables from its outer scope even after the outer function has finished execution.

function outer() {
  let count = 0;

  function inner() {
    count++;
    console.log(count);
  }

  return inner;
}

const counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3

Real uses: Data hiding, private variables, callbacks, memoization, currying.


29. Arrow Functions vs Normal Functions

Normal Function Arrow Function
Has its own this Uses lexical this
Can be used as constructor Cannot be used as constructor
Has arguments object No arguments object
Uses function keyword Uses => syntax

30. Function Declaration vs Expression

Function declarations are fully hoisted and can be called before their definition.
Function expressions follow variable hoisting rules — const/let expressions land in TDZ.

// Declaration — works before definition
greet();
function greet() {
  console.log("Hello");
}

// Expression — ReferenceError if called before
sayHi(); // ❌ ReferenceError
const sayHi = function () {
  console.log("Hi");
};

Variables & Scope

31. let vs var

var let
Function scoped Block scoped
Redeclaration allowed Redeclaration not allowed
Can be updated Can be updated
Hoisted with undefined Hoisted but in TDZ
Old way Modern way (ES6)

The name let comes from mathematical expressions like let x = 10.


32. Temporal Dead Zone

The Temporal Dead Zone (TDZ) is the period where a let or const variable is hoisted but cannot be accessed before initialization.

console.log(a); // undefined — var initializes with undefined
console.log(b); // ReferenceError — b is in TDZ

var a = 10;
let b = 20;

TDZ applies only to let and const, not var.


33. Redeclaring in Switch Blocks

Wrap each case in {} to create separate block scopes and avoid redeclaration errors.

// ❌ Error — same scope
switch (1) {
  case 1:
    let a = 10;
    break;
  case 2:
    let a = 20; // SyntaxError: already declared
    break;
}

// ✅ Correct — separate block scopes
switch (1) {
  case 1: {
    let a = 10;
    break;
  }
  case 2: {
    let a = 20;
    break;
  }
}

34. Block Scope

Variables declared with let and const are accessible only within the {} block where they are defined.

{
  let a = 10;
  const b = 20;
  console.log(a, b); // 10 20
}
console.log(a); // ReferenceError

35. Scope Chain

The scope chain is the process of searching for a variable from the inner scope outward to the global scope.

let a = 10;

function outer() {
  let b = 20;

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

  inner();
}

outer();

Execution Model

36. Execution Context

An execution context is the environment where JavaScript code is evaluated and executed. There are three types:

  1. Global Execution Context (GEC) — created once when the program starts.
  2. Function Execution Context (FEC) — created each time a function is called.
  3. Eval Execution Context — created when eval() is called.

Two phases:

  • Memory Creation Phase — variables/functions are allocated in memory.
  • Execution Phase — code runs line by line.

When the JS file runs, the GEC is created. During the memory phase, var variables are initialized with undefined, let/const stay in TDZ, and function declarations are stored with their full definitions.


37. Memory Creation Phase

The Memory Creation Phase is the first phase of execution context creation where JavaScript allocates memory before executing code.

console.log(a);  // undefined
console.log(b);  // ReferenceError (TDZ)
hello();         // "Hello"

var a = 10;
let b = 20;

function hello() {
  console.log("Hello");
}
Identifier During Memory Phase
a (var) undefined
b (let) TDZ (not accessible)
hello Full function stored

38. Call Stack

The Call Stack is a LIFO (Last In, First Out) data structure used by the JS engine to manage execution contexts and function calls.

  • JS is single-threaded — only one call stack.
  • The most recently called function is always at the top.

39. Synchronous vs Asynchronous

Synchronous — code executes line by line, blocking until each line completes.

console.log(1);
console.log(2);
console.log(3);
// 1, 2, 3

Asynchronous — allows non-blocking operations; the rest of the code continues while waiting.

console.log(1);
setTimeout(() => console.log(2), 1000);
console.log(3);
// 1, 3, 2

40. Event Loop

The Event Loop continuously checks whether the call stack is empty and moves callbacks from the queues to the call stack.

Priority order:

  1. Call Stack (synchronous code)
  2. Microtask Queue (Promises)
  3. Macrotask / Callback Queue (setTimeout, etc.)

41. Macrotask Queue

The Callback Queue (Macrotask Queue) stores callbacks from asynchronous operations, waiting for the call stack to be empty.

Examples: setTimeout, setInterval, DOM events.


42. Microtask Queue

The Microtask Queue is a high-priority queue that stores Promise callbacks and executes them before the callback queue.

Examples: Promise.then, .catch, .finally, queueMicrotask.

Promise.resolve().then(() => {
  console.log("Microtask"); // runs before any setTimeout
});

43. Web APIs

Web APIs are browser features that allow JavaScript to perform asynchronous operations outside the call stack.

Examples: setTimeout, fetch, DOM event listeners, geolocation.


44. setTimeout vs setInterval

setTimeout setInterval
Runs once Runs repeatedly
Single execution Continuous execution
setTimeout(() => console.log("Once"), 1000);
setInterval(() => console.log("Repeat"), 1000);

45. Why setTimeout(fn, 0) Doesn't Execute Immediately

Even with a 0 ms delay, setTimeout first goes to Web APIs, then the Callback Queue, and only executes when the call stack is completely empty.


46. How setTimeout Works Internally

Call Stack → Web API → Callback Queue → Event Loop → Call Stack

Async JavaScript

47. Callbacks

A callback is a function passed into another function to be executed later, usually after completing an asynchronous task.

function fetchData(callback) {
  setTimeout(() => {
    console.log("Data fetched");
    callback();
  }, 2000);
}

function processData() {
  console.log("Processing data");
}

fetchData(processData);
// Data fetched
// Processing data

48. Callback Hell

Callback inside a callback (nested callbacks) creates Callback Hell — deeply nested, hard-to-maintain code.

setTimeout(() => {
  console.log("Step 1");
  setTimeout(() => {
    console.log("Step 2");
    setTimeout(() => {
      console.log("Step 3");
    }, 1000);
  }, 1000);
}, 1000);

49. Promises

A Promise is an object that represents either the success or failure of an asynchronous operation in the future.

const promise = new Promise((resolve, reject) => {
  let success = true;
  if (success) {
    resolve("Data fetched");
  } else {
    reject("Error");
  }
});

promise
  .then((data) => console.log(data))
  .catch((err) => console.log(err));

Promise creation is synchronous; .then() / .catch() handling is asynchronous.

console.log(1);
const p = new Promise((resolve) => {
  console.log(2);   // synchronous — runs immediately
  resolve();
});
p.then(() => console.log(3)); // async — microtask
console.log(4);
// Output: 1, 2, 4, 3

50. Promise States

A Promise can be in exactly one of three states:

State Description
Pending Initial state, neither resolved nor rejected
Fulfilled Operation succeeded (resolve called)
Rejected Operation failed (reject called)

Once a promise settles (fulfilled or rejected), its state cannot change again. Only the first resolve or reject call takes effect.

new Promise((resolve, reject) => {
  resolve("First");
  resolve("Second"); // ignored
  reject("Third");   // ignored
}).then(console.log); // "First"

51. Promise Chaining

Promise chaining executes multiple async operations sequentially using .then(). Each .then() returns a new Promise.

Promise.resolve(5)
  .then((num) => num * 2)   // 10
  .then((result) => console.log(result)); // 10
step1()
  .then((result) => {
    console.log(result);
    return step2(result);
  })
  .then((result) => {
    console.log(result);
    return step3(result);
  })
  .then(console.log)
  .catch(console.log);

52. Promise.all / allSettled / race / any

Method Behavior
Promise.all Resolves when all succeed; fails on any reject
Promise.allSettled Returns results of all (success + failure)
Promise.race Settles with the first to settle (resolve or reject)
Promise.any Settles with the first to succeed
// Promise.all
Promise.all([Promise.resolve(1), Promise.resolve(2)])
  .then(console.log); // [1, 2]

// Promise.race — first to settle wins
Promise.race([
  new Promise(r => setTimeout(() => r("Slow"), 1000)),
  new Promise(r => setTimeout(() => r("Fast"), 100))
]).then(console.log); // "Fast"

53. async / await

async/await is syntactic sugar over promises that makes async code look synchronous and easier to read.

async function getData() {
  const result = await Promise.resolve("Hello");
  console.log(result); // "Hello"
}
  • An async function always returns a Promise, even for non-Promise values.
  • await pauses the async function until the Promise settles.
async function test() {
  console.log(1);
  await Promise.resolve();
  console.log(2); // runs after current synchronous code
}

test();
console.log(3);
// Output: 1, 3, 2

54. Callbacks vs Promises

Pros of Promises over Callbacks:

  • Better readability (no pyramid of doom)
  • Cleaner error handling (.catch())
  • Supports chaining
  • Works with async/await

Cons of Promises:

  • Slightly steeper learning curve
  • Cannot be easily cancelled
  • Requires understanding of the microtask queue

this Keyword

55. What is this?

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

Context this value
Global scope (browser) window
Normal function (non-strict) window
Normal function (strict mode) undefined
Arrow function Lexical this from enclosing scope
Object method The object itself
Event listener The DOM element
const obj = {
  name: "Dimitri",
  greet() {
    console.log(this.name); // "Dimitri"
  }
};
obj.greet();

56. call(), apply(), bind()

call() — immediately invokes a function with a specific this value.

function greet() {
  console.log(this.name);
}
greet.call({ name: "Dimitri" }); // "Dimitri"

apply() — same as call(), but passes arguments as an array.

function add(a, b) {
  console.log(a + b);
}
add.apply(null, [2, 3]); // 5

bind() — returns a new function with a fixed this (does not call immediately).

const person = { name: "Rahul" };
function greet() {
  console.log("Hello " + this.name);
}
const boundGreet = greet.bind(person);
boundGreet(); // "Hello Rahul"

Objects

57. Object.keys / values / entries

const user = { name: "Dimitri", age: 22 };

Object.keys(user);    // ["name", "age"]
Object.values(user);  // ["Dimitri", 22]
Object.entries(user); // [["name", "Dimitri"], ["age", 22]]

// Looping with entries:
for (const [key, value] of Object.entries(user)) {
  console.log(`${key}: ${value}`);
}

58. Object.freeze vs Object.seal

Object.freeze() Object.seal()
Cannot add / delete / update Cannot add / delete
Fully immutable Existing properties can update
const user = { name: "Rahul", age: 22 };

Object.freeze(user);
user.name = "Aman"; // ignored
user.city = "Delhi"; // ignored

Object.seal(user);
user.age = 25;       // ✅ allowed
user.city = "Delhi"; // ❌ ignored

Note: Both freeze and seal are shallow — nested objects remain mutable.


59. Optional Chaining

Optional chaining (?.) safely accesses nested properties without throwing an error if a property doesn't exist.

const user = {};
console.log(user.address?.city); // undefined (no error)

const student = { marks: { math: 90 } };
console.log(student.marks?.math);    // 90
console.log(student.address?.city);  // undefined

60. Nullish Coalescing Operator

?? returns the right-hand value only when the left-hand value is null or undefined (unlike || which triggers on all falsy values).

console.log(null ?? "Default");      // "Default"
console.log(undefined ?? "Default"); // "Default"
console.log(0 ?? "Default");         // 0 — 0 is not null/undefined
console.log("" ?? "Default");        // "" — empty string is not null/undefined

61. Prototypal Inheritance

Prototypal inheritance is a mechanism where objects can inherit properties and methods from another object through the prototype chain.

const animal = {
  eats: true,
  walk() { console.log("Animal walks"); }
};

const dog = {
  bark() { console.log("Dog barks"); }
};

dog.__proto__ = animal; // dog inherits from animal

console.log(dog.eats); // true
dog.walk();            // "Animal walks"
dog.bark();            // "Dog barks"

Using constructors:

function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = function () {
  console.log(`Hi ${this.name}`);
};

const user = new Person("Dimitri");
user.sayHi(); // "Hi Dimitri"
// Chain: user → Person.prototype → Object.prototype → null

__proto__ vs prototype:

__proto__ prototype
Actual prototype link of an object Property on constructor functions
Lives on object instances Lives on function objects
Used for property lookup Used when creating objects with new

62. Prototype Chain

The prototype chain is the mechanism by which JavaScript traverses through linked prototype objects to look up properties and methods. The chain ends at Object.prototype.__proto__ === null.

// dog → animal → Object.prototype → null
console.log(dog.eats); // found at animal in the chain

63. Garbage Collection

Garbage collection is an automatic memory management process that removes unreachable objects from memory. JavaScript uses a mark-and-sweep algorithm — objects that can no longer be accessed are cleaned up automatically.


Events

64. Events Overview

Events are actions or occurrences in the browser that JavaScript can detect and respond to.

Event Description
click User clicked
input User typed
submit Form submitted
keydown Key pressed
mouseover Mouse hovered
load Page loaded
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
  console.log("Button clicked");
});

addEventListener() is the recommended approach — it supports multiple listeners and better event management.


65. Event Flow & Propagation

Event Flow is the complete path an event travels through the DOM in three phases:

Capturing Phase → Target Phase → Bubbling Phase

Event Propagation is the actual movement of an event through the DOM during these phases.


66. Event Capturing

Event Capturing is the phase where an event travels from the top of the DOM tree down to the target element.

parent.addEventListener("click", () => {
  console.log("Parent Capture");
}, true); // true = capturing phase

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

// Click on child → "Parent Capture", "Child"

Pass true as the third argument to addEventListener to listen in the capturing phase (default is false = bubbling).


67. Event Bubbling

Event Bubbling is the default behavior where an event starts at the target element and bubbles up to parent elements.

parent.addEventListener("click", () => console.log("Parent"));
child.addEventListener("click", () => console.log("Child"));

// Click on child → "Child", "Parent"

68. Event Delegation

Event Delegation attaches a single event listener to a parent element to handle events for its children, using event bubbling.

// Instead of multiple listeners:
// btn1.addEventListener(...), btn2.addEventListener(...)

// Use one parent listener:
const parent = document.getElementById("parent");
parent.addEventListener("click", (event) => {
  console.log(event.target.innerText); // identifies which button was clicked
});

Benefits: Fewer listeners, works for dynamically added elements.


Other Concepts

69. Template Literals

Template literals use backticks and allow embedded expressions with ${}.

const name = "Dimitri";
console.log(`Hello ${name}`); // "Hello Dimitri"

70. Debouncing

Debouncing delays function execution until the user stops triggering events for a specified duration. The timer resets on every new event.

function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

Used in: Search inputs (delay API calls), resize events.


71. Throttling

Throttling ensures a function executes at most once in a specified time interval, even if the event fires repeatedly.

Used in: Scroll events, window resize, button spam prevention.


72. Generators

Generators are special functions that can pause and resume execution using yield.

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

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

73. Truthy & Falsy Values

Falsy values (only these 8 evaluate to false):

false, 0, -0, 0n, "", null, undefined, NaN

Truthy values — everything else, including:

true, 1, -5, "hello", "0",
[], // empty array ← truthy!
{}, // empty object ← truthy!
function(){}

74. Short-Circuit Evaluation

&& (AND) — stops at the first falsy value and returns it.

console.log(0 && "Hello"); // 0
console.log(true && "JS" && 0 && "Done"); // 0

|| (OR) — stops at the first truthy value and returns it.

console.log("" || "Default"); // "Default"
console.log(false || 0 || null || "Hello"); // "Hello"

75. Web Storage & Cookies

localStorage — persists data with no expiration.
sessionStorage — data is cleared when the browser tab closes.
Cookies — small data files stored in the browser, mainly used for authentication, session management, and tracking.

Feature Cookie localStorage sessionStorage
Expiry Manual / set Never Tab close
Size limit ~4KB ~5-10MB ~5-10MB
Sent to server Yes No No
Use case Auth/sessions Persistent data Temporary data

Array Methods

Method Description Example
push() Adds element(s) to the end [1,2].push(3)[1,2,3]
pop() Removes last element [1,2,3].pop()[1,2]
unshift() Adds element(s) to the start [1,2].unshift(0)[0,1,2]
shift() Removes first element [1,2,3].shift()[2,3]
forEach() Loops over array (returns nothing) [1,2,3].forEach(n => console.log(n))
map() Returns new array with transformed elements [1,2,3].map(n => n * 2)[2,4,6]
filter() Returns elements matching a condition [1,2,3,4].filter(n => n % 2 === 0)[2,4]
reduce() Reduces array to a single value [1,2,3].reduce((acc,n) => acc+n, 0)6
includes() Checks if value exists [1,2,3].includes(2)true
indexOf() Returns index of first match [1,2,3].indexOf(2)1
find() Returns first matching element [1,2,3,4].find(n => n > 2)3
slice() Returns a copy of a portion (non-destructive) [1,2,3,4].slice(1,3)[2,3]
splice() Inserts/removes elements in place (mutates) arr.splice(2,1,"X") — removes 1, adds "X"
concat() Merges arrays [1,2].concat([3,4])[1,2,3,4]
join() Joins elements into a string [1,2,3].join("-")"1-2-3"

slice vs splice: slice is non-destructive (returns a copy); splice mutates the original array.


Output-Based Questions

Promise Output Questions

Q1

console.log('Start');
const promise = new Promise((resolve) => {
  console.log('Promise executor');
  resolve('Resolved');
});
promise.then(console.log);
console.log('End');

Output: StartPromise executorEndResolved


Q2

Promise.resolve(1)
  .then(v => { console.log(v); return v + 1; })  // 1
  .then(v => { console.log(v); })                 // 2
  .then(v => { console.log(v); return Promise.resolve(3); }) // undefined
  .then(console.log);                             // 3

Output: 12undefined3


Q3

Promise.resolve(Promise.resolve(Promise.resolve(1)))
  .then(console.log);

Output: 1
Promise.resolve() automatically unwraps nested promises recursively.


Q4

console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');

Output: 1432
Microtasks (Promise) run before macrotasks (setTimeout).


Q5 — Error in chain

Promise.resolve('Start')
  .then(v => { console.log(v); throw new Error('Oops!'); })
  .then(() => console.log('Hello'))      // skipped
  .catch(err => { console.log('Caught:', err.message); return 'Recovered'; })
  .then(console.log);

Output: StartCaught: Oops!Recovered


Q6 — Only first settle counts

new Promise((resolve, reject) => {
  resolve('First');
  resolve('Second'); // ignored
  reject('Third');   // ignored
}).then(console.log);

Output: First


Q7 — Undefined return in chain

new Promise(resolve => resolve(1))
  .then(v => { console.log(v); return 2; })
  .then(v => { console.log(v); })              // no return
  .then(v => { console.log(v); return Promise.reject('Error'); })
  .catch(err => { console.log(err); })
  .then(() => console.log('Done'));

Output: 12undefinedErrorDone


Q8 — async/await

async function test() {
  console.log('1');
  await Promise.resolve();
  console.log('2');
}
console.log('3');
test();
console.log('4');

Output: 3142


Q9 — Promise.all fails fast

Promise.all([Promise.resolve(1), Promise.reject('Error'), Promise.resolve(3)])
  .then(v => console.log('Success:', v))
  .catch(err => console.log('Failed:', err));

Output: Failed: Error


Q10 — Nested promise microtask order

Promise.resolve(1)
  .then(v => {
    console.log(v);
    Promise.resolve(2).then(console.log);
    return 3;
  })
  .then(console.log);

Output: 132
The nested promise creates a separate microtask entry; the outer chain resolves first.


Q11 — Promise.race

Promise.race([
  new Promise(r => setTimeout(() => r("Slow"), 1000)),
  new Promise(r => setTimeout(() => r("Fast"), 100)),
  new Promise((_, rej) => setTimeout(() => rej("Error"), 50)),
]).then(console.log).catch(console.log);

Output: Error (rejects at 50ms — fastest to settle)


Q12 — Multiple catch/then

Promise.reject('First error')
  .catch(e => { console.log('Catch 1:', e); throw new Error('Second error'); })
  .catch(e => { console.log('Catch 2:', e.message); return 'Recovered'; })
  .then(v => { console.log('Then:', v); throw new Error('Third error'); })
  .catch(e => console.log('Catch 3:', e.message));

Output: Catch 1: First errorCatch 2: Second errorThen: RecoveredCatch 3: Third error


Q13 — async functions return promises

async function func1() { return 1; }
async function func2() { return Promise.resolve(2); }

func1().then(console.log);
func2().then(console.log);
console.log(3);

Output: 312


Q14 — finally

Promise.resolve('Success')
  .finally(() => { console.log('Finally 1'); return 'ignored'; })
  .then(v => console.log('Then 1:', v))
  .finally(() => { console.log('Finally 2'); throw new Error('Finally error'); })
  .then(v => console.log('Then 2:', v)) // skipped
  .catch(e => console.log('Catch:', e.message));

Output: Finally 1Then 1: SuccessFinally 2Catch: Finally error
finally does not change the resolved value but can throw errors that break the chain.


Event Loop Output Questions

Q15 — Macro & Micro interleaving

console.log('Start');
setTimeout(() => {
  console.log('Timeout 1');
  Promise.resolve().then(() => console.log('Promise in Timeout'));
}, 0);
Promise.resolve()
  .then(() => {
    console.log('Promise 1');
    setTimeout(() => console.log('Timeout in Promise'), 0);
  })
  .then(() => console.log('Promise 2'));
setTimeout(() => console.log('Timeout 2'), 0);
console.log('End');

Output: StartEndPromise 1Promise 2Timeout 1Promise in TimeoutTimeout 2Timeout in Promise


Q16 — Promise pending, .then queued only on resolve

// .then() only enters microtask queue when the promise is resolved/rejected.
// If the promise is still pending, the callback is stored internally — not queued yet.

const promise = new Promise((resolve) => {
  console.log('Executor start');
  setTimeout(() => { console.log('Timeout in executor'); resolve('Done'); }, 0);
  console.log('Executor end');
});
promise.then(v => console.log('Then:', v));
console.log('After promise creation');

Output: Executor startExecutor endAfter promise creationTimeout in executorThen: Done


Q17 — Promise with reject and chaining

console.log(1);
setTimeout(() => console.log(2), 10);
setTimeout(() => console.log(3), 0);

new Promise((_, reject) => {
  console.log(4);
  reject(5);
  console.log(6);
})
  .then(() => console.log(7))    // skipped (rejected)
  .catch(() => console.log(8))   // handles reject
  .then(() => console.log(9))    // continues
  .catch(() => console.log(10))  // skipped
  .then(() => console.log(11))
  .then(console.log)             // undefined
  .finally(() => console.log(12));

console.log(13);

Output: 146138911undefined1232


Q18

console.log(1);
setTimeout(() => {
  console.log(3);
  Promise.resolve().then(() => console.log(4));
}, 0);
Promise.resolve().then(() => {
  console.log(5);
  setTimeout(() => console.log(7), 0);
});
console.log(6);

Output: 165347


Q19

console.log('A');
setTimeout(() => {
  console.log('B');
  Promise.resolve().then(() => {
    console.log('C');
    setTimeout(() => console.log('D'), 0);
  });
}, 0);
Promise.resolve().then(() => {
  console.log('E');
  setTimeout(() => {
    console.log('F');
    Promise.resolve().then(() => console.log('G'));
  }, 0);
});
setTimeout(() => console.log('H'), 0);
console.log('I');

Output: AIEBCHFGD


Q20

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => console.log(3)).then(() => console.log(4));
}, 0);
setTimeout(() => {
  console.log(5);
  Promise.resolve().then(() => console.log(6));
}, 0);
Promise.resolve()
  .then(() => { console.log(7); setTimeout(() => console.log(8), 0); })
  .then(() => console.log(9));
console.log(10);

Output: 1079234568


Q21

setTimeout(() => {
  console.log('Timeout 1');
  Promise.resolve()
    .then(() => {
      console.log('Promise 1');
      setTimeout(() => {
        console.log('Timeout 2');
        Promise.resolve().then(() => console.log('Promise 2'));
      }, 0);
    })
    .then(() => console.log('Promise 3'));
}, 0);
console.log('Start');

Output: StartTimeout 1Promise 1Promise 3Timeout 2Promise 2


Q22

console.log('Start');
Promise.resolve().then(() => { console.log('P1'); setTimeout(() => console.log('T1'), 0); });
setTimeout(() => { console.log('T2'); Promise.resolve().then(() => console.log('P2')); }, 0);
Promise.resolve().then(() => { console.log('P3'); setTimeout(() => console.log('T3'), 0); });
setTimeout(() => { console.log('T4'); Promise.resolve().then(() => console.log('P4')); }, 0);
console.log('End');

Output: StartEndP1P3T2P2T4P4T1T3


Q23

console.log(1);
setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3);
    setTimeout(() => { console.log(4); Promise.resolve().then(() => console.log(5)); }, 0);
  });
  setTimeout(() => console.log(6), 0);
}, 0);
Promise.resolve().then(() => {
  console.log(7);
  setTimeout(() => { console.log(8); Promise.resolve().then(() => console.log(9)); }, 0);
});
console.log(10);

Output: 11072389645

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment