JavaScript interview revision notes
Interview-focused revision. Written for developers preparing for an interview who want to refresh their JavaScript fast. Free, no sign-up.
49 topics in these notes
- What is JavaScript?
- JavaScript Variables (var, let, const)
- JavaScript Data Types
- Functions (declaration, expression, arrow)
- Truthy & Falsy in JavaScript
- Hoisting
- Scope & Closure
- this keyword
- call, apply, bind
- Arrays (key methods)
- Objects & destructuring
- Spread, rest, template literals
- == vs === (Equality)
- Promises & async/await
- Error handling (try/catch)
- Prototypes & inheritance
- Event loop
- Strict mode
- ES6 features (10 key)
- Callback
- Callback hell
- Null and Undefined
- Array (basics)
- Array operations
- Objects (basics)
- Class
- Map, Set, WeakMap, WeakSet
- Stack & Queue
- Regular Expressions (RegExp)
- JSON
- String methods
- Number, NaN, Infinity
- Optional chaining & nullish coalescing
- Iterators & Generators
- IIFE
- Loops (for, for...in, for...of, while)
- Recursion
- Shallow & deep copy
- Debounce & Throttle
- Error types & custom errors
- Date
- Symbol
- BigInt
- Rest parameters & arguments
- Getters & setters
- Currying
- Object static methods
- Property descriptors
- ES Modules (import / export)
What is JavaScript?
Simple real-life example (Understand first — very important for understanding)
- Think of a web page that is just text and images—nothing moves or reacts.
- Now think of a page where buttons show popups, forms validate, and content updates without reload.
That "alive" behavior on the web is powered by JavaScript. Real-world idea: one language to make pages interactive and to run on servers.
Technical definition
- JavaScript is a high-level, interpreted, multi-paradigm language (procedural, OOP, functional).
- Runs in the browser (front-end) and on the server (via Node.js).
- Standardized as ECMAScript (ES5, ES6/ES2015, etc.).
- "Java" and "JavaScript" are different languages; only the name is similar.
Simple practical example
- Show a greeting when user clicks a button.
- Ask for name and update text on the page without refreshing.
Code example
// Frontend: show a greeting when user clicks a button
const button = document.getElementById("greetBtn");
const message = document.getElementById("greetMsg");
button.addEventListener("click", () => {
const name = prompt("What is your name?");
const safeName = name && name.trim() ? name.trim() : "Guest";
message.textContent = "Hello, " + safeName + " 👋";
});Why it matters
- Almost every web application uses JavaScript somewhere.
- Understanding where it runs (browser vs Node.js) helps you decide what should happen on client vs server.
Interview tip
- "JavaScript is the language that makes web pages interactive, runs in both browser and Node.js, and powers modern frameworks like React and Express."
JavaScript Variables (var, let, const)
Simple real-life example (Understand first — very important for understanding)
- Think of labeled jars on a shelf: "Sugar", "Salt", "Flour".
- The label (name) tells you what is inside (value). Some jars you never change; others you refill.
Variables are named containers for values. Real-world idea: use the right kind of jar—fixed or refillable—so your code stays clear and safe.
Technical definition
var: function-scoped, can be re-declared, hoisted; avoid in modern JS.let: block-scoped, can be reassigned but not re-declared in same scope.const: block-scoped, cannot be reassigned (but object/array contents can change).- Use
constby default; useletonly when you need reassignment.
Simple practical example
- Use const for configuration values that never change.
- Use let for loop counters or values that need to be updated.
Code example
var oldVar = 1; // function-scoped, avoid
let counter = 0; // can reassign
const API_URL = "https://api.example.com"; // cannot reassign
for (let i = 0; i < 3; i++) {
counter += i;
}
// API_URL = "..."; // TypeError in strict modeWhy it matters
- Choosing between var/let/const affects bugs related to scope and reassignment.
- Modern JS codebases and interviewers expect you to prefer let/const over var.
Interview tip
- When asked difference between var, let and const: talk about scope, hoisting, and reassignment; say you mostly use const, then let, and almost never var.
JavaScript Data Types
Simple real-life example (Understand first — very important for understanding)
- A phone number on paper = one simple value you can copy.
- A contact in your phone (name, numbers, notes) = a bundle of related data.
Some values are simple and copied as-is; others are bundles that you refer to from different places. Real-world idea: know which is which so you avoid surprises when copying or comparing.
Technical definition
- Primitive types: string, number, boolean, null, undefined, symbol, bigint.
- Reference types: object, array, function, Date, etc.
- Primitives are stored by value; objects/arrays are stored by reference.
- typeof null is "object" (legacy JS bug).
- Use Array.isArray() for arrays; typeof for primitives and functions.
Simple practical example
- Copying a number makes a new, independent value.
- Copying an object variable makes a new reference to the same underlying object.
Code example
let age = 25;
let user = { name: "Alex", city: "Delhi" };
let ageCopy = age; ageCopy++;
let userCopy = user; userCopy.city = "Mumbai";
console.log(age, ageCopy);
console.log(user.city);Why it matters
- Many bugs come from not understanding value vs reference.
- Mutating shared objects can affect code in unexpected places.
Interview tip
- Be ready to list primitive types and explain value vs reference with a tiny example.
Functions (declaration, expression, arrow)
Simple real-life example (Understand first — very important for understanding)
- Like a recipe: you follow the steps once, and you can reuse the same recipe anytime.
- You can give it ingredients (inputs) and get a result (return value).
Functions are reusable steps that take inputs and can return a result. Real-world idea: name them clearly and use them so you don't repeat the same steps everywhere.
Technical definition
- Function declaration: function add(a, b) { ... } (hoisted).
- Function expression: const add = function(a, b) { ... } (not hoisted).
- Arrow function: const add = (a, b) => a + b; (no own this or arguments; lexical this).
- Use arrow functions for callbacks and small utilities.
Simple practical example
- Reusable discount calculation for many prices.
- Use arrow function for a small utility passed into map.
Code example
const applyDiscount = (amount, percent) => amount - (amount * percent) / 100;
const cart = [100, 250, 50];
const finalAmounts = cart.map(price => applyDiscount(price, 10));
console.log(finalAmounts);Why it matters
- Functions are the main building block of JS.
- Clean, small functions with clear inputs/outputs are easier to test and reuse.
Interview tip
- Explain declaration vs expression vs arrow; mention hoisting and this behavior for arrow functions.
Truthy & Falsy in JavaScript
Simple real-life example (Understand first — very important for understanding)
- A switch treats "power on" and "power off" differently.
- In conditions, some values count as "on" (true) and some as "off" (false)—even if they're not literally true or false.
Real-world idea: the language treats certain values as true or false in conditions. Knowing which ones are "off" helps you validate input and avoid bugs.
Technical definition
- Falsy: false, 0, -0, 0n, "", null, undefined, NaN.
- Everything else is truthy.
- Use === instead of == to avoid unwanted coercion.
Simple practical example
- Empty string should be treated as invalid input.
- null/undefined should also be rejected.
Code example
function isValidInput(value) {
return value !== null && value !== undefined && value.trim() !== "";
}
if (!isValidInput("")) console.log("Please enter a username");Why it matters
- Many bugs are due to unexpected truthy/falsy values.
- Knowing the falsy list helps you write safer input checks.
Interview tip
- List all falsy values and show a tiny example with an if check or input validation.
Hoisting
Simple real-life example (Understand first — very important for understanding)
- Like a teacher reading the class list before everyone has arrived—they know the names in advance.
- The language "sees" some names before it runs the code line by line.
Real-world idea: declarations are processed first in a scope. That's why you can sometimes use a name before the line where it's written—and why the rules differ for different kinds of declarations.
Technical definition
- var and function declarations are hoisted; let/const are in a temporal dead zone until their line.
- Only declarations are hoisted, not assignments.
- Function expressions are not hoisted like declarations.
Code example
console.log(a); // undefined
var a = 10;
// console.log(b); // ReferenceError
let b = 20;
greet(); // "Hi"
function greet() { console.log("Hi"); }Why it matters
- Using variables before declaration gives undefined or ReferenceError depending on var vs let/const.
Interview tip
- Hoisting is JS moving declarations to the top of scope; var is hoisted and initialized with undefined; let/const are in temporal dead zone.
Scope & Closure
Simple real-life example (Understand first — very important for understanding)
- Like rooms in a building: what you say in one room doesn't automatically apply in another.
- A function can "remember" things from the place it was created, even when used elsewhere.
Real-world idea: where a variable lives (scope) and what a function can still see (closure) control what your code can access. That keeps parts of the program from stepping on each other.
Technical definition
- Scope: where a variable is visible (global, function, block).
- Closure: a function that remembers variables from its outer scope even after that outer function has returned.
- Use closures for data privacy, factory functions, callbacks.
Code example
function createCounter() {
let count = 0;
return function() { count++; return count; };
}
const counter = createCounter();
console.log(counter(), counter(), counter()); // 1, 2, 3Why it matters
- Closures are everywhere in JS (event handlers, callbacks, React hooks).
Interview tip
- Closure = function + lexical environment. Give counter or private variable example and one use case.
this keyword
Simple real-life example (Understand first — very important for understanding)
- "This" in a sentence depends on who is speaking: "I built this house" means the speaker's house.
- In code, "this" refers to the object or context the function is running for.
Real-world idea: "this" points to the current context—often the object that "owns" the function. It changes depending on how the function is called.
Technical definition
- In a method: this = that object.
- In a regular function: this = global or undefined in strict mode.
- In an arrow function: this is lexically bound (from outer scope).
- call, apply, bind set this explicitly.
Code example
const user = { name: "Alex", greet: function() { console.log("Hello, " + this.name); } };
user.greet(); // Hello, Alex
const greetFn = user.greet;
greetFn(); // undefined (this lost)
user.greet.bind(user)(); // Hello, AlexWhy it matters
- Many bugs come from this being different than expected in callbacks or event handlers.
Interview tip
- this depends on how the function is called — method, standalone, arrow (outer this), and bind/call/apply.
call, apply, bind
Simple real-life example (Understand first — very important for understanding)
- Like lending your recipe to a friend: they can use it in their kitchen (different "this") or you can say "use my kitchen" (fix "this").
- call, apply, and bind let you choose what "this" is and how you pass the ingredients.
Real-world idea: these are ways to call a function with a specific context and arguments. Use them when you need to pass a method around or fix what "this" means.
Technical definition
- fn.call(thisArg, arg1, arg2) — invoke immediately with thisArg and individual args.
- fn.apply(thisArg, [arg1, arg2]) — same as call but args as an array.
- fn.bind(thisArg) — returns a new function with this fixed; does not call immediately.
Code example
function introduce(greeting, punctuation) { console.log(greeting + ", I'm " + this.name + punctuation); }
const person = { name: "Sam" };
introduce.call(person, "Hi", "!");
introduce.apply(person, ["Hello", "."]);
const bound = introduce.bind(person);
bound("Hey", "!!");Why it matters
- When you pass a method as a callback, it loses its original this; bind is the classic fix.
Interview tip
- call and apply invoke with a given this and args (call: list, apply: array). bind returns a new function with this fixed.
Arrays (key methods)
Simple real-life example (Understand first — very important for understanding)
- Like a numbered row of boxes: first, second, third… You can add, remove, or reorder.
- You often loop over them or use methods that find, filter, or transform items.
Real-world idea: arrays hold ordered lists. Knowing the main methods (map, filter, find, reduce) lets you work with data without writing loops every time.
Technical definition
- map(fn) — returns new array; each element is fn(element).
- filter(fn) — returns new array with elements where fn is truthy.
- reduce(fn, initial) — returns single value; fn(acc, item).
- find/findIndex, some/every, forEach for side effects.
Code example
const prices = [100, 200, 50, 300];
const withTax = prices.map(p => p * 1.18);
const expensive = prices.filter(p => p >= 100);
const total = prices.reduce((sum, p) => sum + p, 0);Why it matters
- Most list operations in modern JS use these methods; map/filter return new arrays (good for immutability).
Interview tip
- Be ready to implement filter then map or a sum with reduce; mention that map/filter don't mutate the original array.
Objects & destructuring
Simple real-life example (Understand first — very important for understanding)
- Like a form with labeled fields: name, email, age. Each label has one value.
- You can read, add, or change values by name instead of by position.
Real-world idea: objects group related data under named keys. Destructuring is a shortcut to pull out the fields you need.
Technical definition
- Destructuring: const { a, b } = obj; or const [x, y] = arr; to unpack values.
- Default values: const { name = "Guest" } = obj; Rest: const { a, ...rest } = obj.
- Shorthand: { name, age } when key and variable name match.
Code example
const user = { id: 1, name: "Alex", role: "admin" };
const { name, role } = user;
const [first, second] = [10, 20, 30];
const { name: userName, country = "India" } = user;Why it matters
- Destructuring makes code shorter and clearer; used everywhere in modern JS and React (props, state).
Interview tip
- Explain object and array destructuring; mention default values and renaming (name: userName) with one-line examples.
Spread, rest, template literals
Simple real-life example (Understand first — very important for understanding)
- Spread is like copying a list of items so you can add or merge without changing the original.
- Rest is like saying "and everything else goes here" when you collect the remaining items.
Real-world idea: spread and rest make it easy to copy, merge, and collect values. Template literals let you write strings with variables and line breaks in a clearer way.
Technical definition
- Spread (...): expand iterable — [...arr] (copy), { ...obj } (copy/merge).
- Rest (... in params): collect remaining arguments — function(a, b, ...rest) {}.
- Template literals: backticks, ${expression} for interpolation, multi-line strings.
Why it matters
Spread and rest simplify copying and merging without mutating. Template literals avoid messy string concatenation.
Code example
const a = [1, 2, 3]; const b = [...a, 4];
const opts = { ...defaults, theme: "dark" };
function sum(...nums) { return nums.reduce((s, n) => s + n, 0); }
console.log(`Hello, ${name}! Score: ${10 + 5}`);Interview tip
Spread expands; rest collects. Give one example of spread (copy array or merge object) and one of rest (variable arguments). Mention template literals.
== vs === (Equality)
Simple real-life example (Understand first — very important for understanding)
- Strict comparison: "same type and same value"—like matching a key to a lock.
- Loose comparison can convert types first, which can surprise you.
Real-world idea: use strict equality (===) so you don't get hidden type conversions. Know the difference when reading or debugging code.
Technical definition
- == (loose): compares after type coercion (e.g. 5 == "5" is true).
- === (strict): no coercion; same value and type (5 === "5" is false).
- Use === by default. Object.is(a, b) — like === but Object.is(NaN, NaN) is true.
Why it matters
Bugs from == often involve 0, "", null, undefined. Linters usually require ===. Common interview question.
Code example
console.log(5 == "5", 5 === "5");
console.log(null == undefined); // true
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // trueInterview tip
== coerces types; === does not. Prefer ===. Mention gotcha like 0 == '' or null == undefined, and Object.is for NaN.
Promises & async/await
Simple real-life example (Understand first — very important for understanding)
- Like ordering food: you get a promise of food later; you don't stand there until it's ready.
- You say what to do when it arrives (then) or when something goes wrong (catch).
Real-world idea: async work (network, timers) is handled with promises. async/await lets you write the "when it's done" steps in a straight line instead of nested callbacks.
Technical definition
- Promise: value that may be available now, later, or never. States: pending, fulfilled, rejected.
- new Promise((resolve, reject) => {}); .then / .catch / .finally.
- async function returns a Promise; await pauses until promise settles. Use try/catch for errors.
Why it matters
Most I/O in JS is asynchronous. Promises and async/await avoid callback hell and make error handling clear.
Code example
const p = new Promise((resolve) => setTimeout(() => resolve("Done!"), 100));
p.then(msg => console.log(msg));
async function fetchUser(id) {
try {
const res = await fetch("/api/users/" + id);
return await res.json();
} catch (err) { throw err; }
}Interview tip
Explain Promise states and that async/await is syntactic sugar over promises. Mention try/catch with await and that async functions always return a Promise.
Error handling (try/catch)
Simple real-life example (Understand first — very important for understanding)
- When something goes wrong, you don't want the whole app to crash—you want to catch it and respond.
- Like a safety net: try the risky step, catch the problem, and optionally clean up (finally).
Real-world idea: try/catch lets you handle errors in one place. Use it for sync code; for async, use try/catch with await or .catch() on promises.
Technical definition
- try { } catch (err) { } finally { } — catch synchronous errors.
- throw new Error("message"); Promise rejections are not caught by try/catch unless you await inside try or use .catch().
- finally runs whether or not an error was thrown.
Why it matters
Without try/catch, a thrown error can crash the script. In async code, combine try/catch with await so rejections are caught.
Code example
function parseJSON(str) {
try { return JSON.parse(str); }
catch (e) { console.error("Invalid JSON:", e.message); return null; }
finally { console.log("parse attempt done"); }
}Interview tip
try/catch is for synchronous errors; for async use try/catch with await or .catch(). Say what finally is for.
Prototypes & inheritance
Simple real-life example (Understand first — very important for understanding)
- Like a family trait: children inherit features from their parent without copying everything.
- When you ask an object for a property, it can look on itself first, then on its "parent" chain.
Real-world idea: objects can share behavior through a prototype chain. Classes in JS are a clear way to set up this sharing.
Technical definition
- Every object has [[Prototype]] (Object.getPrototypeOf()). When a property is not found, JS looks up the prototype chain.
- Functions have a prototype property used when called with new; class/extends is syntactic sugar.
Why it matters
Understanding prototypes explains how inheritance and method sharing work in JS.
Code example
function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { console.log(this.name + " makes a sound"); };
const dog = new Animal("Rex");
dog.speak();
console.log(Object.getPrototypeOf(dog) === Animal.prototype);Interview tip
Objects inherit from their prototype. When we use 'new', the new object's prototype is the constructor's prototype property. Class syntax is built on this.
Event loop
Simple real-life example (Understand first — very important for understanding)
- Like a single clerk handling tasks: they finish one thing, then take the next from the queue.
- Some tasks are "do this right after the current one" (microtasks); others are "do this when you get a chance" (macrotasks).
Real-world idea: JS runs one thing at a time. The event loop decides what runs next. Microtasks (e.g. promises) run before the next big task (e.g. setTimeout).
Technical definition
- JS is single-threaded. Event loop takes tasks from call stack and task queue (and microtask queue).
- Microtasks (Promises, queueMicrotask) run after current script and before next macrotask (e.g. setTimeout).
- Order: sync code → microtasks → next macrotask → microtasks.
Why it matters
"What is the output?" questions often rely on event loop order. Microtasks run before the next macrotask.
Code example
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Output: 1, 4, 3, 2Interview tip
JS is single-threaded with an event loop; mention call stack, task queue, and microtasks. Give order-of-execution example (setTimeout vs Promise).
Strict mode
Simple real-life example (Understand first — very important for understanding)
- Like turning on stricter rules so small mistakes become visible instead of silent.
- Some old, loose behaviors are turned off so you write safer code.
Real-world idea: strict mode helps avoid bugs by making certain mistakes throw errors. Modern code and modules use it by default.
Technical definition
- Enable with "use strict"; at top of file or function.
- Disallows: undeclared variables, deleting variables, duplicate parameters; this in non-method calls becomes undefined.
- Modules and many bundlers use strict mode by default.
Why it matters
Strict mode prevents accidental globals and other unsafe patterns.
Code example
"use strict";
let x = 10;
function showThis() { console.log(this); } // undefined in strict
showThis();Interview tip
Strict mode: 'use strict'. Disallows undeclared variables and makes this undefined in plain function calls. Modules are strict by default.
ES6 features (10 key)
Simple real-life example (Understand first — very important for understanding)
- Newer JS added clearer ways to write variables, functions, and data structures.
- let/const, arrow functions, classes, and template strings are part of everyday code now.
Real-world idea: ES6+ features make code shorter and easier to read. Interviewers often ask you to use or explain them.
Technical definition
- let & const — block-scoped.
- Arrow functions — () => {}; lexical this.
- Template literals — `${expr}`.
- Destructuring — const { a, b } = obj; const [x, y] = arr.
- Spread & rest — ....
- Default parameters — function f(a = 1) {}.
- Classes — class X extends Y { }.
- Modules — import/export.
- Promises.
- for...of.
Why it matters
ES6 is the baseline for modern JS; interviews ask "name some ES6 features" or "difference between let and var."
Code example
const name = "Alex";
const greet = (n) => `Hello, ${n}!`;
const [first, ...rest] = [1, 2, 3];
function sum(a = 0, b = 0) { return a + b; }Interview tip
List 5–10: let/const, arrow functions, template literals, destructuring, spread/rest, classes, modules, Promises, default params, for...of.
Callback
Simple real-life example (Understand first — very important for understanding)
- Like leaving your number: "Call me when it's ready." You don't wait; you get called back.
- A callback is a function you pass so someone else can run it later.
Real-world idea: callbacks are the classic way to handle "when this is done." They're still used in many APIs and older code.
Technical definition
- Callback: function passed as argument to be run later (e.g. when async work finishes).
- Common in: addEventListener, setTimeout, fs.readFile, map/filter.
- Node convention: (err, result). Nesting many callbacks = callback hell.
Why it matters
Understanding callbacks is the foundation for async JS; Promises and async/await avoid deep nesting.
Code example
setTimeout(() => console.log("Done!"), 1000);
[1, 2, 3].map((x) => x * 2);Interview tip
Callback is a function passed to be called later. Used in events, timers, I/O. Node uses (err, result). Promises/async-await replace nested callbacks.
Callback hell
Simple real-life example (Understand first — very important for understanding)
- When "call me when A is done" leads to "then call me when B is done" and so on—nested deeper and deeper.
- The code becomes hard to read and to fix.
Real-world idea: nested callbacks are hard to follow. Promises and async/await are the modern way to keep async steps readable.
Technical definition
- Callback hell: many nested callbacks, hard to read and maintain.
- Solutions: Promises (then/catch or async/await), named functions, or async libraries.
- async/await makes sequential async code look like synchronous code.
Why it matters
Interviewers ask "what is callback hell and how do you avoid it?" — answer: Promises and async/await.
Code example
// Prefer async/await:
const user = await getUser(id);
const orders = await getOrders(user.id);
const items = await getItems(orders[0].id);Interview tip
Callback hell is deep nesting of callbacks. Fix with Promises and async/await, or by extracting named functions.
Null and Undefined
Simple real-life example (Understand first — very important for understanding)
- "Nothing here" can mean "not set yet" (undefined) or "intentionally empty" (null).
- Both mean "no value," but people use them for slightly different situations.
Real-world idea: use undefined for "not assigned"; use null when you want to say "this is intentionally empty." Checking for both avoids surprises.
Technical definition
- undefined: variable declared but not assigned; missing property; function with no return.
- null: intentional "no value" — often set by programmer or APIs.
- Both falsy; null == undefined is true; === does not. value == null catches both.
Why it matters
Many bugs from not distinguishing "never set" (undefined) vs "explicitly empty" (null).
Code example
let a; console.log(a); // undefined
const user = { name: "Alex", address: null };
console.log(null == undefined); // true
console.log(null === undefined); // falseInterview tip
undefined = not assigned or missing; null = intentional empty. Use == null to check both. Prefer === for strict checks.
Array (basics)
Simple real-life example (Understand first — very important for understanding)
- An ordered list: first item, second item, and so on. You add, remove, and look up by position.
- Indexes start at zero—like the first shelf in a row.
Real-world idea: arrays are the main way to store and work with lists. Get comfortable with length, index, and basic methods.
Technical definition
- Ordered, zero-indexed; arr.length; create: [], Array.from().
- Access arr[i]; out-of-range gives undefined. push, pop, shift, unshift, splice.
- Reference types — use spread or slice() for shallow copy.
Why it matters
Arrays are used everywhere; basics plus map/filter/reduce are standard interview topics.
Code example
const arr = [10, 20, 30];
arr.push(40); arr.pop();
const copy = [...arr];
Array.from("hello");Interview tip
Arrays: zero-indexed, .length, push/pop/shift/unshift. Reference type — copy with spread or slice.
Array operations
Simple real-life example (Understand first — very important for understanding)
- Transform each item (map), keep only some (filter), or combine them into one value (reduce).
- These methods replace many manual loops and keep intent clear.
Real-world idea: map, filter, and reduce are the bread-and-butter tools for list data. Use them instead of writing loops when they fit.
Technical definition
- push/pop (end), unshift/shift (start), splice(i, deleteCount, ...items).
- indexOf, lastIndexOf, includes, find, findIndex; sort(fn), reverse(); slice, concat, [...arr].
Why it matters
Choosing the right operation affects performance and readability.
Code example
const arr = [3, 1, 4, 1, 5];
arr.splice(2, 1, 99);
arr.indexOf(1); arr.includes(5);
arr.sort((a,b) => a - b);
const copy = arr.slice();Interview tip
Know push/pop (stack), shift/unshift (queue), splice, indexOf/includes/find, sort/reverse. slice and spread for copy.
Objects (basics)
Simple real-life example (Understand first — very important for understanding)
- A collection of named values: name, age, email. You read and write by key.
- You can loop over keys, values, or entries when you need to.
Real-world idea: objects store key-value pairs. Know how to create, read, update, and iterate over them.
Technical definition
- Key-value pairs; obj.key or obj["key"]; bracket for dynamic keys.
- Object.keys(obj), Object.values(obj), Object.entries(obj); for (const k in obj).
- obj.x = 1; delete obj.x; "key" in obj; obj.hasOwnProperty("key").
Why it matters
Objects are the main data structure for structured data.
Code example
const user = { name: "Alex", age: 25 };
Object.keys(user); Object.entries(user);
for (const key in user) { if (user.hasOwnProperty(key)) console.log(key, user[key]); }Interview tip
Objects: key-value, dot or bracket access. Object.keys/values/entries for iteration.
Class
Simple real-life example (Understand first — very important for understanding)
- Like a blueprint: you define the shape (properties and methods) once.
- Then you create many instances that follow that blueprint.
Real-world idea: classes are a clear way to define object types and inheritance. They sit on top of the prototype system.
Technical definition
- ES6 class is syntactic sugar over prototypes; constructor runs when new ClassName() is called.
- extends for inheritance; super() must be called in subclass constructor before using this.
- static methodName() {}; get prop() {}; set prop(v) {}; #privateField (ES2022).
Why it matters
Classes are the standard way to do OOP in modern JS; interviews ask about extends, super, and prototypes.
Code example
class Person { constructor(name) { this.name = name; } greet() { return "Hello, " + this.name; } }
class Student extends Person { constructor(name, grade) { super(name); this.grade = grade; } }
const s = new Student("Alex", 10); s.greet();Interview tip
class is syntax over prototypes. constructor, extends, super(). Call super() before this in subclass. Static methods live on the class.
Map, Set, WeakMap, WeakSet
Simple real-life example (Understand first — very important for understanding)
- Map: like an object that uses any value as key and keeps insertion order.
- Set: like a list that never stores the same value twice—good for unique items.
Real-world idea: use Map when you need keys that aren't strings or need order; use Set when you need a unique collection.
Technical definition
- Map: key-value; keys any type; set, get, has, delete, size; iteration via forEach or for...of.
- Set: unique values; add, has, delete, size; use for deduplication.
- WeakMap/WeakSet: keys objects only; entries can be GC'd when key has no other reference; no iteration, no size.
Code example
const map = new Map();
map.set("name", "Alex"); map.set(1, "one");
const set = new Set([1, 2, 2, 3]); set.add(4);
[...set]; // dedupeInterview tip
Map: any key type, .size, insertion order. Set: unique values. WeakMap/WeakSet: keys objects, no iteration, GC-friendly.
Stack & Queue
Simple real-life example (Understand first — very important for understanding)
- Stack: last in, first out—like a stack of plates. You add and remove from the top.
- Queue: first in, first out—like a line. You add at the back, take from the front.
Real-world idea: stack and queue are two simple patterns for ordering data. They show up in algorithms and in real code.
Technical definition
- Stack: LIFO — push(item), pop(). Use array: push/pop.
- Queue: FIFO — enqueue (push), dequeue (shift). shift is O(n).
Code example
const stack = []; stack.push(1); stack.push(2); stack.pop();
const queue = []; queue.push("a"); queue.push("b"); queue.shift();Interview tip
Stack: LIFO, push/pop. Queue: FIFO, push + shift. Mention shift is O(n) if asked about performance.
Regular Expressions (RegExp)
Simple real-life example (Understand first — very important for understanding)
- Like a search pattern: "starts with a digit," "has an @ in the middle," "three letters then a number."
- You use it to find, validate, or replace text that matches the pattern.
Real-world idea: regular expressions describe text patterns. Use them for validation and search; keep them simple or document them.
Technical definition
- Create: /pattern/flags or new RegExp('pattern', 'gi'). Flags: g, i, m.
- .test(str), .exec(str); str.match(regex), str.replace(regex, repl), str.split(regex).
- \d digit, \w word, \s space; + * ?; [a-z]; ^ $; capture groups ().
Code example
"a1b22c".match(/\d+/g);
"hello".replace(/l/g, "L");
/^[a-z]+@[a-z]+\.[a-z]+$/i.test("a@b.co");Interview tip
Know test, exec, match, replace; flags g and i; character classes and quantifiers; capture groups for replace ($1, $2).
JSON
Simple real-life example (Understand first — very important for understanding)
- Like a standard format for writing down data so different systems can read it.
- You turn objects into a string to send or store, then parse the string back when you need it.
Real-world idea: JSON is the common format for APIs and config. You stringify to send and parse to read.
Technical definition
- JSON.stringify(obj), JSON.parse(str). Replacer/reviver for custom serialization.
- Only serializes: objects, arrays, strings, numbers, booleans, null. Not: functions, undefined, Symbol, Date (becomes string), circular refs (throws).
Code example
JSON.stringify({ a: 1, b: "x" });
JSON.parse('{"a":1}');Interview tip
stringify/parse; replacer/reviver; circular refs and undefined/Date limitations.
String methods
Simple real-life example (Understand first — very important for understanding)
- Strings have built-in helpers: split into parts, trim spaces, change case, find a substring.
- You use them to clean and check user input or to build messages.
Real-world idea: string methods let you work with text without writing everything from scratch. Know the main ones for interviews and daily use.
Technical definition
- indexOf, lastIndexOf, includes, startsWith, endsWith; slice, substring; split(sep); trim, toLowerCase, toUpperCase, repeat, padStart, padEnd; replace (first only unless regex g), replaceAll.
Code example
" hi ".trim();
"abc".includes("b"); "abc".padStart(5, "0");
"a-b-c".split("-");Interview tip
Know includes, startsWith, endsWith, slice, split, trim, replace; strings are immutable.
Number, NaN, Infinity
Simple real-life example (Understand first — very important for understanding)
- When a math operation has no sensible number result (e.g. 0/0), you get a special "not a number" value.
- It's the only value that isn't equal to itself—so you check with isNaN() or Number.isNaN().
Real-world idea: NaN means the result of a number operation wasn't a real number. Check for it when doing math or parsing numbers.
Technical definition
- parseInt(str, radix), parseFloat(str). Number.isNaN(x) (prefer over isNaN — isNaN coerces), Number.isFinite(x), Number.isInteger(x).
- NaN !== NaN; use Number.isNaN. Math.floor, ceil, round, max, min, random, abs.
Code example
parseInt("10", 10); parseInt("10px");
Number.isNaN(NaN); isNaN("hello");
Math.floor(2.7);Interview tip
NaN !== NaN; use Number.isNaN. parseInt second arg for radix. isNaN vs Number.isNaN — coercion.
Optional chaining & nullish coalescing
Simple real-life example (Understand first — very important for understanding)
- Like asking "if there is a door, open it" instead of "open the door" when the door might not exist.
- You use ?. so the code stops safely when something in the chain is null or undefined.
Real-world idea: optional chaining (?.) lets you read nested properties without writing long if-checks. If any part is missing, you get undefined instead of an error.
Technical definition
- obj?.prop, obj?.[key], fn?.() — short-circuits to undefined if obj is null/undefined.
- a ?? b — nullish coalescing: use b only when a is null or undefined. Unlike ||, 0 and "" are kept. a ??= b for assign when null/undefined.
Code example
o?.a?.b;
null ?? 5; // 5
0 ?? 5; // 0Interview tip
?. for safe property/method access; ?? for defaults when null/undefined only; difference from || (0 and '').
Iterators & Generators
Simple real-life example (Understand first — very important for understanding)
- An iterator is like a bookmark: you can ask for the "next" item until there are no more.
- A generator is a function that yields values one at a time, so you can loop over them or pull on demand.
Real-world idea: iterators and generators let you work with sequences lazily—one step at a time—which can save memory and keep code clear.
Technical definition
- Iterator: object with next() returning { value, done }. Iterable: has Symbol.iterator. for...of uses iterables.
- Generator: function* gen() { yield 1; }; returns iterator; lazy evaluation.
Code example
function* ids() { yield 1; yield 2; }
const it = ids();
it.next(); it.next(); it.next();Interview tip
Iterator: next() → { value, done }. Iterable: Symbol.iterator. Generator: function*, yield; lazy and composable.
IIFE
Simple real-life example (Understand first — very important for understanding)
- Like running a small recipe once as soon as you open the book—you don't save it for later.
- A function that is defined and called immediately, often to create a private scope.
Real-world idea: IIFE runs code once and can hide variables from the rest of the script. Older code used it a lot; modules and block scope often replace it now.
Technical definition
- Immediately Invoked Function Expression: (function() { })(); runs once, creates private scope.
- Used historically to avoid polluting global and to capture loop variables (before let/const).
Code example
(function() {
const secret = 10;
console.log(secret);
})();Interview tip
IIFE: define and run in one go; creates scope; used for privacy and old-style modules.
Loops (for, for...in, for...of, while)
Simple real-life example (Understand first — very important for understanding)
- for: "do this a fixed number of times" or "go through each index."
- for...of: "go through each value in a list." for...in: "go through each key in an object."
Real-world idea: pick the loop that matches what you're doing—by index, by value, or by key—so the code is clear and you avoid off-by-one errors.
Technical definition
- for (i=0; i<n; i++), while (cond), do { } while (cond).
- for...in — enumerable property keys (object); use Object.hasOwn to skip inherited; avoid for arrays.
- for...of — iterable values (array, string, Map, Set). break, continue, label for nested loops.
Code example
for (const x of [1, 2, 3]) console.log(x);
const o = { a: 1, b: 2 };
for (const k in o) console.log(k, o[k]);Interview tip
for...in = keys (object); for...of = values (iterable). Don't use for...in for arrays if you care about order.
Recursion
Simple real-life example (Understand first — very important for understanding)
- Like a task that breaks into smaller versions of the same task: "solve for n by solving for n-1 and combining."
- You need a base case (when to stop) and a recursive case (call itself with a smaller input).
Real-world idea: recursion is when a function calls itself. Use it when the structure of the problem is self-similar (trees, divide-and-conquer).
Technical definition
- Function calls itself; needs base case and recursive case. Tail recursion: last action is recursive call.
- Use for tree/graph traversal, divide-and-conquer, factorial, flatten. Watch stack overflow for deep recursion.
Code example
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
factorial(5); // 120Interview tip
Always have a base case; explain stack; mention tail recursion and stack overflow for very deep recursion.
Shallow & deep copy
Simple real-life example (Understand first — very important for understanding)
- Shallow copy: copy the top level; anything nested is still shared (like copying a folder but not the files inside).
- Deep copy: copy everything at every level so nothing is shared.
Real-world idea: copying objects isn't always "full copy." Know when you need a shallow vs deep copy so changes don't surprise you.
Technical definition
- Shallow: [...arr], { ...obj }, Object.assign({}, obj). Nested objects still shared.
- Deep: JSON.parse(JSON.stringify(obj)) — loses functions, Date, undefined, circular. structuredClone(obj) handles many types and circular; not functions.
Code example
const a = [{ x: 1 }]; const b = [...a]; b[0].x = 2; a[0].x; // 2
const c = structuredClone(a); c[0].x = 3; a[0].x; // still 2Interview tip
Shallow: spread, slice, assign — nested shared. Deep: structuredClone or JSON round-trip; know limitations.
Debounce & Throttle
Simple real-life example (Understand first — very important for understanding)
- Debounce: "run this only after they've stopped doing it for a moment" (e.g. search after typing stops).
- Throttle: "run this at most every X time" (e.g. scroll handler once per 100 ms).
Real-world idea: both limit how often something runs. Debounce waits for quiet; throttle spreads calls out over time. Use them for resize, scroll, or input.
Technical definition
- Debounce: run only after no calls for X ms; resets on each call. Use: search on type, resize.
- Throttle: run at most once every X ms. Use: scroll, mousemove, rate-limited API. Implement with setTimeout + clearTimeout (debounce); timestamp or flag (throttle).
Code example
function debounce(fn, ms) {
let t;
return function(...args) { clearTimeout(t); t = setTimeout(() => fn.apply(this, args), ms); };
}
function throttle(fn, ms) {
let last = 0;
return function(...args) { const now = Date.now(); if (now - last >= ms) { last = now; fn.apply(this, args); } };
}Interview tip
Debounce: wait for silence then run (search). Throttle: run at most every N ms (scroll). Know how to implement both.
Error types & custom errors
Simple real-life example (Understand first — very important for understanding)
- Errors have types (SyntaxError, TypeError, ReferenceError) so you know what went wrong.
- You can throw your own errors with a message and catch them to handle specific cases.
Real-world idea: use the right error type and message so debugging is easier. Catch, log or handle, and optionally rethrow.
Technical definition
- Built-in: Error, TypeError, ReferenceError, SyntaxError, RangeError. throw new Error('msg').
- Custom: class MyError extends Error { constructor(m) { super(m); this.name = 'MyError'; } }. Use instanceof in catch. finally runs always.
Code example
throw new TypeError("Expected number");
try { JSON.parse("invalid"); } catch (e) { console.log(e instanceof SyntaxError); }Interview tip
Know Error, TypeError, ReferenceError, SyntaxError; custom errors extend Error; try/catch/finally; finally runs always.
Date
Simple real-life example (Understand first — very important for understanding)
- Dates are a single "moment in time" stored inside the computer.
- You create, format, and compare them—but watch out for time zones and daylight saving.
Real-world idea: Date is the built-in way to work with time. For serious date logic or time zones, libraries like date-fns or Luxon help.
Technical definition
- new Date(), new Date(ms), new Date(year, month, day...) — month 0-indexed.
- getFullYear, getMonth, getDate, getHours, getTime; toISOString(), toLocaleString(); Date.parse(str).
Code example
const d = new Date();
d.getTime(); d.toISOString();
new Date(0); // 1970-01-01 UTCInterview tip
Month 0–11; getTime() for timestamp; toISOString for JSON/APIs; timezone gotchas (UTC vs local).
Symbol
Simple real-life example (Understand first — very important for understanding)
- Like a unique label that can't clash with any other label—even if the name is the same.
- Useful for object properties that should stay hidden or unique.
Real-world idea: symbols give you unique keys. They're used for well-known protocols (e.g. iteration) and for hiding implementation details.
Technical definition
- Symbol() — unique value every time. Symbol('desc') for debugging. Use as object keys for uniqueness.
- Symbol.for('key') — global registry. Well-known: Symbol.iterator, Symbol.toStringTag, Symbol.toPrimitive.
Code example
const s = Symbol("id");
const o = { [s]: 1, name: "x" };
o[s]; // 1
Object.keys(o); // ["name"]Interview tip
Symbol = unique id; good for object keys to avoid collision; Symbol.iterator for custom iteration.
BigInt
Simple real-life example (Understand first — very important for understanding)
- When normal numbers are too big (e.g. beyond 2^53), you need a different type.
- BigInt lets you do integer math on arbitrarily large whole numbers.
Real-world idea: use BigInt when you need integers larger than the safe range. You can't mix them with normal numbers without converting.
Technical definition
- BigInt(123) or 123n. Cannot mix with Number in same expression without converting. Use when beyond Number.MAX_SAFE_INTEGER (2^53 - 1). JSON.stringify drops BigInt unless custom replacer.
Code example
const n = 9007199254740993n;
n + 1n;
Number(n); // precision lossInterview tip
BigInt for integers > 2^53-1; 123n or BigInt(); can't mix with Number; JSON needs custom replacer.
Rest parameters & arguments
Simple real-life example (Understand first — very important for understanding)
- "Give me the first item, and put everything else in a bag."
- Rest (...rest) collects the remaining arguments or array elements into one array.
Real-world idea: rest parameters let functions accept "and any number more" arguments. Spread is the opposite—unpacking an array into separate items.
Technical definition
- Rest: function f(...args) — args is a real array of all extra arguments. Prefer over arguments.
- arguments: array-like, exists in non-arrow functions. Rest must be last parameter.
Code example
function sum(...nums) { return nums.reduce((a, b) => a + b, 0); }
sum(1, 2, 3); // 6Interview tip
...rest is real array; arguments is array-like and only in non-arrow; prefer rest.
Getters & setters
Simple real-life example (Understand first — very important for understanding)
- Like a receptionist: you don't touch the filing cabinet directly; you ask for a value or request a change.
- Getters and setters run code when someone reads or writes a property.
Real-world idea: getters/setters hide how a value is stored and let you validate or compute on read/write. They make properties behave like methods.
Technical definition
- get prop() { return this._x; }, set prop(v) { this._x = v; }. Access like normal property. Same in class body. Object.defineProperty(obj, 'prop', { get, set }).
Code example
const o = { _n: 0, get n() { return this._n; }, set n(v) { if (typeof v === 'number') this._n = v; } };
o.n = 10; o.n;Interview tip
get/set look like properties but run code; use for validation or computed values.
Currying
Simple real-life example (Understand first — very important for understanding)
- Like a function that takes one ingredient at a time: first the base, then the flavor, then you get the result.
- Each call returns a new function that waits for the next argument.
Real-world idea: currying turns a multi-argument function into a chain of one-argument steps. It's useful for reuse and composition.
Technical definition
- Currying: function that takes one (or more) arg and returns function taking the next, until all args collected. f(a)(b)(c).
- Partial application: fix some args, return function for the rest. Use for configuration, composition.
Code example
const add = a => b => a + b;
add(1)(2); // 3
const add1 = add(1); add1(5); // 6Interview tip
Currying: f(a)(b) instead of f(a,b); returns function until all args supplied; useful for partial application.
Object static methods
Simple real-life example (Understand first — very important for understanding)
- Some methods belong to the "blueprint" itself, not to each instance—like a factory rule that applies to all products.
- Object.keys(), Object.assign(), and Object.freeze() are examples.
Real-world idea: static methods live on the constructor or Object itself. They're tools for working with objects and types.
Technical definition
- Object.keys(obj), Object.values(obj), Object.entries(obj), Object.fromEntries(entries).
- Object.assign(target, ...sources) — mutates target. Object.freeze(obj), Object.seal(obj). Object.create(proto), Object.hasOwn(obj, key).
Code example
Object.entries({ a: 1, b: 2 });
Object.fromEntries([["a", 1]]);
Object.freeze({ x: 1 });Interview tip
keys, values, entries, fromEntries; assign for merge/shallow copy; freeze/seal for immutability; create for prototype; hasOwn for own props.
Property descriptors
Simple real-life example (Understand first — very important for understanding)
- Each property has hidden settings: can it be written? listed? changed or deleted?
- You can define or read them to make properties read-only, non-enumerable, etc.
Real-world idea: descriptors control how a property behaves. Use them when you need fine control over what code can do with an object.
Technical definition
- Each property has: value, writable, enumerable, configurable. Accessor: get, set (no value/writable).
- Object.getOwnPropertyDescriptor(obj, 'key'), Object.defineProperty(obj, 'key', descriptor). enumerable controls keys() and for...in.
Code example
const o = {};
Object.defineProperty(o, 'x', { value: 1, writable: false, enumerable: true });
o.x = 2; // no effect in strict; o.x stays 1Interview tip
Descriptor: value, writable, enumerable, configurable; get/set for accessors; defineProperty/getOwnPropertyDescriptor.
ES Modules (import / export)
Simple real-life example (Understand first — very important for understanding)
- Like a standard way to say "use this from that file" and "this file exports these things."
- import and export are the keywords; the system loads modules and avoids naming clashes.
Real-world idea: ES modules are the modern way to split and share code. They run in strict mode and have a clear dependency graph.
Technical definition
- Export: export const x = 1; export function f() {}; export default class C {}. One default per file.
- Import: import x from './m.js' (default); import { a, b } from './m.js' (named); import * as M from './m.js' (namespace).
- Modules are strict, own scope, async load. Browser: script type="module"; Node: type: module or .mjs.
Code example
// utils.js
export const add = (a, b) => a + b;
export default function greet() { return 'Hi'; }
// app.js
import greet, { add } from './utils.js';Interview tip
export/export default; import default, import { named }; ES modules have own scope, strict, async load. type=module in browser; type:module or .mjs in Node.
Practise the questions
Read the top 50 JavaScript interview questions and answers to test yourself after revising.
More Frontend notes
HTML · CSS · JavaScript in the Browser · TypeScript · React · Angular · All developer notes