JavaScript Fundamentals — Interview Questions
🟢 JavaScript Fundamentals
Section titled “🟢 JavaScript Fundamentals”Core language fundamentals — data types, variables, scoping, functions, closures, and this.
Q1. What are the different data types in JavaScript?
Section titled “Q1. What are the different data types in JavaScript?”Answer: JavaScript has 8 data types:
Primitive Types:
String—"hello"Number—42,3.14BigInt—9007199254740991nBoolean—true/falseUndefined— variable declared but not assignedNull— intentional absence of valueSymbol— unique identifier
Non-Primitive:
Object— includes Arrays, Functions, Dates, etc.
typeof "hello" // "string"typeof 42 // "number"typeof true // "boolean"typeof undefined // "undefined"typeof null // "object" ← known JS quirktypeof Symbol() // "symbol"typeof {} // "object"typeof function(){} // "function"Q2. What is the difference between var, let, and const?
Section titled “Q2. What is the difference between var, let, and const?”Answer:
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (undefined) | Yes (TDZ) | Yes (TDZ) |
| Re-declare | Yes | No | No |
| Re-assign | Yes | Yes | No |
var x = 1;var x = 2; // ✅ allowed
let y = 1;let y = 2; // ❌ SyntaxError
const z = 1;z = 2; // ❌ TypeErrorQ3. What is == vs === in JavaScript?
Section titled “Q3. What is == vs === in JavaScript?”Answer:
==(loose equality) — performs type coercion before comparison===(strict equality) — checks both value and type
0 == "0" // true (coercion happens)0 === "0" // false (different types)
null == undefined // truenull === undefined // falseBest Practice: Always use
===to avoid unexpected bugs.
Q4. What is null vs undefined?
Section titled “Q4. What is null vs undefined?”Answer:
undefined— a variable has been declared but not assigned a valuenull— an intentional empty value, explicitly set by the developer
let a;console.log(a); // undefined
let b = null;console.log(b); // null
typeof null // "object" (JS quirk)typeof undefined // "undefined"
null == undefined // truenull === undefined // falseQ5. What is hoisting in JavaScript?
Section titled “Q5. What is hoisting in JavaScript?”Answer: Hoisting is JavaScript’s behavior of moving declarations to the top of their scope before code executes.
vardeclarations are hoisted and initialized withundefinedfunctiondeclarations are fully hoistedletandconstare hoisted but stay in the Temporal Dead Zone (TDZ)
console.log(x); // undefined (not error)var x = 5;
console.log(y); // ❌ ReferenceErrorlet y = 5;
greet(); // ✅ "Hello" — function fully hoistedfunction greet() { console.log("Hello"); }
hello(); // ❌ TypeError — only variable is hoisted, not the functionvar hello = function() { console.log("Hello"); };Q6. What are arrow functions and how do they differ from regular functions?
Section titled “Q6. What are arrow functions and how do they differ from regular functions?”Answer:
// Regular functionfunction add(a, b) { return a + b; }
// Arrow functionconst add = (a, b) => a + b;Key Differences:
- Arrow functions do not have their own
this - Arrow functions cannot be used as constructors
- Arrow functions have no
argumentsobject - Arrow functions cannot be used as generator functions
const obj = { name: "Alice", regular: function() { console.log(this.name); }, // "Alice" arrow: () => { console.log(this.name); } // undefined};Q7. What is a closure in JavaScript?
Section titled “Q7. What is a closure in JavaScript?”Answer: A closure is a function that retains access to its outer scope even after the outer function has returned.
function counter() { let count = 0; return function() { count++; return count; };}
const increment = counter();console.log(increment()); // 1console.log(increment()); // 2console.log(increment()); // 3The inner function “closes over” the
countvariable.
Common use cases: Module pattern, memoization, event handlers, debouncing/throttling.
Q8. What is the difference between function declaration and function expression?
Section titled “Q8. What is the difference between function declaration and function expression?”Answer:
// Function Declaration — hoisted fullygreet(); // ✅ worksfunction greet() { return "Hello"; }
// Function Expression — not hoistedhello(); // ❌ TypeErrorconst hello = function() { return "Hello"; };
// Named Function Expressionconst greetUser = function sayHello() { return "Hello";};Q9. What is typeof and what are its quirks?
Section titled “Q9. What is typeof and what are its quirks?”Answer:
typeof 42 // "number"typeof "hello" // "string"typeof true // "boolean"typeof undefined // "undefined"typeof null // "object" ← bug in JS (historical)typeof [] // "object" ← use Array.isArray()typeof {} // "object"typeof function(){} // "function"typeof Symbol() // "symbol"typeof 42n // "bigint"Q10. What is NaN and how do you check for it?
Section titled “Q10. What is NaN and how do you check for it?”Answer:
NaN (Not-a-Number) is returned when a math operation fails.
console.log(0 / 0); // NaNconsole.log("abc" * 2); // NaN
// Quirk: NaN is not equal to itself!NaN === NaN // false
// Correct ways to checkNumber.isNaN(NaN) // true ✅Number.isNaN("abc") // false ✅ (does NOT coerce)isNaN("abc") // true ← coerces, unreliableQ11. What are truthy and falsy values?
Section titled “Q11. What are truthy and falsy values?”Answer:
Falsy values (only 8):
false, 0, -0, 0n, "", '', ``, null, undefined, NaNEverything else is truthy, including:
"0" // truthy (non-empty string)[] // truthy (empty array){} // truthy (empty object)-1 // truthy"false" // truthyif ([]) console.log("truthy!"); // ✅ printsif ({}) console.log("truthy!"); // ✅ printsQ12. What is short-circuit evaluation?
Section titled “Q12. What is short-circuit evaluation?”Answer:
// && returns first falsy OR last valuefalse && "hello" // falsetrue && "hello" // "hello"true && true // true
// || returns first truthy OR last valuefalse || "hello" // "hello""hi" || "hello" // "hi"false || false // false
// Nullish coalescing ?? — only triggers on null/undefinednull ?? "default" // "default"undefined ?? "default" // "default"0 ?? "default" // 0 (not null/undefined)"" ?? "default" // "" (not null/undefined)Q13. What is strict mode?
Section titled “Q13. What is strict mode?”Answer:
"use strict";
// Benefits:x = 10; // ❌ ReferenceError (undeclared var)delete Object.prototype; // ❌ TypeErrorfunction(a, a) {} // ❌ duplicate params errorthis // undefined in functions (not global)Q14. What is the difference between deep copy and shallow copy?
Section titled “Q14. What is the difference between deep copy and shallow copy?”Answer:
const obj = { a: 1, b: { c: 2 } };
// Shallow copy — nested objects share referenceconst shallow = { ...obj };shallow.b.c = 99;console.log(obj.b.c); // 99 ← mutated!
// Deep copy methods:
// 1. JSON (simple but limited — no functions/undefined)const deep = JSON.parse(JSON.stringify(obj));
// 2. structuredClone (modern, recommended)const deep2 = structuredClone(obj);
// 3. Lodashimport _ from "lodash";const deep3 = _.cloneDeep(obj);Q15. What is this in JavaScript? (4 binding rules)
Section titled “Q15. What is this in JavaScript? (4 binding rules)”Answer:
this refers to the execution context of a function.
Rule 1: Default binding — global (or undefined in strict mode)
function show() { console.log(this); }show(); // Window / undefined(strict)Rule 2: Implicit binding — object before the dot
const obj = { name: "Alice", greet() { console.log(this.name); } };obj.greet(); // "Alice"
// Binding lost:const fn = obj.greet;fn(); // undefinedRule 3: Explicit binding — call/apply/bind
show.call({ name: "Bob" }); // {name:"Bob"}Rule 4: new binding — creates new object
function Person(name) { this.name = name; }const p = new Person("Alice");p.name; // "Alice"Priority: new > explicit > implicit > default
Q16. What is the difference between call, apply, and bind?
Section titled “Q16. What is the difference between call, apply, and bind?”Answer:
All three set this explicitly.
function greet(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`;}
const user = { name: "Alice" };
// call — invokes immediately, args comma-separatedgreet.call(user, "Hello", "!"); // "Hello, Alice!"
// apply — invokes immediately, args as arraygreet.apply(user, ["Hello", "!"]); // "Hello, Alice!"
// bind — returns new function, does NOT invokeconst boundGreet = greet.bind(user, "Hello");boundGreet("!"); // "Hello, Alice!"
// Real use case: method borrowingconst obj1 = { name: "Bob", greet() { return this.name; } };const obj2 = { name: "Carol" };obj1.greet.call(obj2); // "Carol"Q17. What is IIFE (Immediately Invoked Function Expression)?
Section titled “Q17. What is IIFE (Immediately Invoked Function Expression)?”Answer:
// Classic IIFE(function() { const privateVar = "I am private"; console.log(privateVar);})();
// Arrow IIFE(() => { console.log("Arrow IIFE");})();
// With parameters((name) => { console.log(`Hello, ${name}`);})("World");
// Use cases:// - Avoid polluting global scope (pre-ES6 modules)// - Create isolated scope// - One-time initializationQ18. What is Object.freeze() vs Object.seal()?
Section titled “Q18. What is Object.freeze() vs Object.seal()?”Answer:
// Object.freeze() — cannot add, delete, or MODIFY propertiesconst frozen = Object.freeze({ name: "Alice", age: 30 });frozen.name = "Bob"; // ❌ silently failsfrozen.city = "NYC"; // ❌ cannot adddelete frozen.age; // ❌ cannot delete
// Object.seal() — cannot add or delete, but CAN modifyconst sealed = Object.seal({ name: "Alice", age: 30 });sealed.name = "Bob"; // ✅ modification allowedsealed.city = "NYC"; // ❌ cannot adddelete sealed.age; // ❌ cannot delete
// ⚠️ Both are SHALLOW — nested objects are NOT frozen/sealedQ19. What is globalThis?
Section titled “Q19. What is globalThis?”Answer:
// Before globalThis — different globals per environment// Browser: window// Node.js: global// Web Worker: self
// globalThis — unified way to access global objectconsole.log(globalThis); // works in all environmentsglobalThis.myGlobal = "hello";Q20. What is try...catch...finally?
Section titled “Q20. What is try...catch...finally?”Answer:
try { const result = JSON.parse("invalid json");} catch (error) { console.error("Error:", error.message);} finally { console.log("This always runs");}
// Custom errorfunction divide(a, b) { if (b === 0) throw new Error("Division by zero"); return a / b;}
try { divide(10, 0);} catch (e) { console.log(e.message); // "Division by zero"}Q21. What is JSON.stringify and JSON.parse?
Section titled “Q21. What is JSON.stringify and JSON.parse?”Answer:
const obj = { name: "Alice", age: 30 };
// Object → JSON stringconst json = JSON.stringify(obj);// '{"name":"Alice","age":30}'
// JSON string → Objectconst parsed = JSON.parse(json);
// Pretty printJSON.stringify(obj, null, 2);
// Note: undefined, functions, symbols are omittedJSON.stringify({ a: undefined, b: () => {} }); // '{}'Q22. What is currying in JavaScript?
Section titled “Q22. What is currying in JavaScript?”Answer: Currying transforms a function with multiple arguments into a series of functions each taking one argument.
// Curry utilityfunction curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } return function(...nextArgs) { return curried.apply(this, args.concat(nextArgs)); }; };}
const add = (a, b, c) => a + b + c;const curriedAdd = curry(add);curriedAdd(1)(2)(3); // 6
// Real use caseconst multiply = curry((factor, value) => factor * value);const double = multiply(2);const triple = multiply(3);[1, 2, 3].map(double); // [2, 4, 6]Q23. What is function composition?
Section titled “Q23. What is function composition?”Answer:
// compose — right to leftconst compose = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x);
// pipe — left to rightconst pipe = (...fns) => x => fns.reduce((v, fn) => fn(v), x);
const trim = str => str.trim();const lower = str => str.toLowerCase();const split = str => str.split(" ");
const process = pipe(trim, lower, split);process(" Hello World "); // ["hello", "world"]Q24. What is memoization?
Section titled “Q24. What is memoization?”Answer:
function memoize(fn) { const cache = new Map(); return function(...args) { const key = JSON.stringify(args); if (cache.has(key)) { console.log("cache hit"); return cache.get(key); } const result = fn.apply(this, args); cache.set(key, result); return result; };}
// Examplefunction fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2);}
const fastFib = memoize(fibonacci);fastFib(40); // fast!Q25. What is Object.defineProperty()?
Section titled “Q25. What is Object.defineProperty()?”Answer:
const person = {};
Object.defineProperty(person, "name", { value: "Alice", writable: false, // cannot reassign enumerable: true, // shows in for...in, Object.keys configurable: false // cannot delete or redefine});
person.name = "Bob"; // silently fails (or TypeError in strict)person.name; // "Alice"
// Getters/setters with definePropertyObject.defineProperty(person, "fullName", { get() { return `${this.first} ${this.last}`; }, set(value) { [this.first, this.last] = value.split(" "); }, enumerable: true, configurable: true});Q26. What is Object.getOwnPropertyDescriptor?
Section titled “Q26. What is Object.getOwnPropertyDescriptor?”Answer:
const obj = { name: "Alice" };Object.getOwnPropertyDescriptor(obj, "name");// {// value: "Alice",// writable: true,// enumerable: true,// configurable: true// }
// Immutable constantObject.defineProperty(obj, "ID", { value: 12345, writable: false, enumerable: false, configurable: false});
// Get ALL descriptorsObject.getOwnPropertyDescriptors(obj);Q27. Implement Function.prototype.bind from scratch.
Section titled “Q27. Implement Function.prototype.bind from scratch.”Answer:
Function.prototype.myBind = function(thisArg, ...outerArgs) { const originalFn = this;
function BoundFunction(...innerArgs) { // Handle `new` call — new overrides bind's thisArg if (this instanceof BoundFunction) { return new originalFn(...outerArgs, ...innerArgs); } return originalFn.apply(thisArg, [...outerArgs, ...innerArgs]); }
// Maintain prototype chain for new if (originalFn.prototype) { BoundFunction.prototype = Object.create(originalFn.prototype); }
return BoundFunction;};Q28. How does the new keyword work internally?
Section titled “Q28. How does the new keyword work internally?”Answer:
// When you do: const obj = new MyClass(args)// JavaScript does these steps:function myNew(Constructor, ...args) { // 1. Create empty object with Constructor's prototype const obj = Object.create(Constructor.prototype);
// 2. Bind `this` to new object and call constructor const result = Constructor.apply(obj, args);
// 3. Return the new object (unless constructor returns an object) return (result !== null && typeof result === "object") ? result : obj;}
function Person(name, age) { this.name = name; this.age = age;}
const alice = myNew(Person, "Alice", 30);alice instanceof Person; // truealice.name; // "Alice"Q29. What is tail call optimization (TCO)?
Section titled “Q29. What is tail call optimization (TCO)?”Answer:
// Regular recursion — stack overflow risk at large nfunction factorial(n) { if (n <= 1) return 1; return n * factorial(n - 1); // ← NOT a tail call}
// Tail call — last operation is the recursive callfunction factorialTCO(n, acc = 1) { if (n <= 1) return acc; return factorialTCO(n - 1, n * acc); // ← tail call}
// With TCO, engine reuses the current stack frame// ES6 spec mandates TCO in strict mode — only Safari implements it// Alternative: trampolinefunction trampoline(fn) { return function(...args) { let result = fn(...args); while (typeof result === "function") result = result(); return result; };}Q30. How do you handle circular references in JSON serialization?
Section titled “Q30. How do you handle circular references in JSON serialization?”Answer:
// Problemconst obj = { name: "Alice" };obj.self = obj; // circular referenceJSON.stringify(obj); // ❌ TypeError
// Solution: replacer functionfunction circularStringify(obj) { const seen = new WeakSet(); return JSON.stringify(obj, (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) return "[Circular]"; seen.add(value); } return value; });}Q31. Implement Object.assign from scratch.
Section titled “Q31. Implement Object.assign from scratch.”Answer:
function myAssign(target, ...sources) { if (target === null || target === undefined) { throw new TypeError("Cannot convert undefined or null to object"); }
const to = Object(target);
for (const source of sources) { if (source === null || source === undefined) continue;
// Only own enumerable properties for (const key of Object.keys(source)) { to[key] = source[key]; }
// Also copy Symbol properties for (const sym of Object.getOwnPropertySymbols(source)) { if (Object.prototype.propertyIsEnumerable.call(source, sym)) { to[sym] = source[sym]; } } }
return to;}Q32. Implement a deep clone function from scratch.
Section titled “Q32. Implement a deep clone function from scratch.”Answer:
function deepClone(value, visited = new WeakMap()) { // Handle primitives and null if (value === null || typeof value !== "object") return value;
// Handle circular references if (visited.has(value)) return visited.get(value);
// Handle special types if (value instanceof Date) return new Date(value.getTime()); if (value instanceof RegExp) return new RegExp(value.source, value.flags); if (value instanceof Set) { const clone = new Set(); visited.set(value, clone); value.forEach(item => clone.add(deepClone(item, visited))); return clone; } if (value instanceof Map) { const clone = new Map(); visited.set(value, clone); value.forEach((v, k) => clone.set(deepClone(k, visited), deepClone(v, visited))); return clone; }
// Handle arrays and objects const clone = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value)); visited.set(value, clone);
for (const key of Reflect.ownKeys(value)) { clone[key] = deepClone(value[key], visited); }
return clone;}