JavaScript
1. What is JavaScript?
JavaScript is a lightweight, interpreted, dynamically-typed scripting language that runs inside web browsers (and on servers via Node.js). It makes web pages interactive — responding to clicks, validating forms, updating content without page reloads.
| Feature | JavaScript | Java |
|---|---|---|
| Type | Scripting language | Object-oriented programming language |
| Typing | Dynamic (types checked at runtime) | Static (types checked at compile time) |
| Compiled? | Interpreted (by browser/Node.js) | Compiled to bytecode, run on JVM |
| Where it runs | Browser + Node.js | JVM (any platform) |
| Syntax style | C-like but loosely typed | Strictly typed, verbose |
| Relation | None — the name is purely a marketing decision. They are completely different languages. | |
let, const, arrow functions, classes, template literals.
2. Data Types & typeof
JavaScript has 7 primitive types and 1 structural type:
| Type | Example | typeof result |
|---|---|---|
| string | "hello", 'world' | "string" |
| number | 42, 3.14, NaN, Infinity | "number" |
| boolean | true, false | "boolean" |
| undefined | undefined (variable declared, not assigned) | "undefined" |
| null | null (intentional empty value) | "object" ⚠️ (historic bug) |
| symbol | Symbol("id") | "symbol" |
| bigint | 9007199254740991n | "bigint" |
| object | {}, [], null | "object" |
| function | function() {} | "function" |
typeof "hello" // "string" typeof 42 // "number" typeof true // "boolean" typeof undefined // "undefined" typeof null // "object" ← famous JavaScript bug, memorise this! typeof NaN // "number" ← NaN is technically a number type! typeof [] // "object" ← arrays are objects typeof function(){} // "function"
typeof null === "object" and typeof NaN === "number" are the two most-tested typeof surprises. Memorise both.
3. var vs let vs const
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function scope | Block scope {} | Block scope {} |
| Hoisted? | Yes — initialised as undefined | Yes — but in TDZ (not usable) | Yes — but in TDZ (not usable) |
| Re-declare same scope? | ✅ Yes | ❌ No | ❌ No |
| Reassign value? | ✅ Yes | ✅ Yes | ❌ No |
| Must initialise? | No | No | ✅ Yes |
// var — function scoped function test() { if (true) { var x = 10; } console.log(x); // 10 — var leaks out of the if block! } // let — block scoped function test2() { if (true) { let y = 20; } console.log(y); // ReferenceError: y is not defined } // const — cannot reassign const PI = 3.14; PI = 3; // TypeError: Assignment to constant variable // const with objects — the reference is fixed, but contents can change const obj = { a: 1 }; obj.a = 99; // ✅ allowed — changing a property obj = {}; // ❌ not allowed — reassigning the variable
var declarations are moved to the top of their function and set to undefined. let and const are hoisted but sit in the Temporal Dead Zone (TDZ) — accessing them before declaration throws a ReferenceError, not undefined.
// var hoisting — classic exam question console.log(a); // undefined (hoisted, but not yet assigned) var a = 5; console.log(a); // 5 // let TDZ — throws error console.log(b); // ReferenceError: Cannot access 'b' before initialization let b = 5;
4. Operators
Arithmetic Operators
+ // addition (also string concatenation) - // subtraction * // multiplication / // division % // modulo (remainder): 10 % 3 → 1 ** // exponentiation: 2 ** 3 → 8 ++ // increment: x++ (post), ++x (pre) -- // decrement let x = 5; console.log(x++); // 5 — returns THEN increments console.log(x); // 6 console.log(++x); // 7 — increments THEN returns
Comparison: == vs ===
// == (loose equality) — converts types before comparing 1 == "1" // true — string "1" is converted to number 1 0 == false // true — false converts to 0 null == undefined // true // === (strict equality) — NO type conversion 1 === "1" // false — different types 0 === false // false — different types null === undefined // false
=== in real code. The exam tests == heavily because its type coercion produces surprising results. When in doubt: == converts types, === does not.
Logical & Ternary Operators
// Logical true && false // false (AND — both must be true) true || false // true (OR — one must be true) !true // false (NOT) // Ternary — shorthand if/else // condition ? valueIfTrue : valueIfFalse let age = 18; let status = age >= 18 ? "adult" : "minor"; console.log(status); // "adult"
5. Type Coercion — The #1 Tricky Topic
JavaScript automatically converts values from one type to another. This is called implicit type coercion and it causes many surprising results.
+ operator: if either operand is a string, it concatenates. All other arithmetic operators (-, *, /, %) convert strings to numbers first.
// + with strings = concatenation "1" + 2 // "12" — number 2 becomes string "2" 1 + "2" // "12" — same result "5" + 3 + 2 // "532" — left to right: "5"+3="53", "53"+2="532" 5 + 3 + "2" // "82" — 5+3=8 first (numbers), then 8+"2"="82" // - * / always convert to number "5" - 3 // 2 — "5" becomes number 5 "6" * "2" // 12 — both become numbers "10" / "2" // 5 "abc" - 1 // NaN — "abc" can't be a number // Boolean coercion true + 1 // 2 — true becomes 1 false + 1 // 1 — false becomes 0 true + true // 2 // null and undefined null + 1 // 1 — null becomes 0 undefined + 1 // NaN // Floating point trap 0.1 + 0.2 // 0.30000000000000004 — NOT 0.3! 0.1 + 0.2 == 0.3 // false — floating point precision issue
6. Functions
// 1. Function Declaration — hoisted, can be called before it's written function greet(name) { return "Hello, " + name; } console.log(greet("Darshan")); // "Hello, Darshan" // 2. Function Expression — NOT hoisted const add = function(a, b) { return a + b; }; console.log(add(3, 4)); // 7 // 3. Arrow Function (ES6) — shorter syntax const multiply = (a, b) => a * b; console.log(multiply(3, 4)); // 12 // Arrow with body block (multiple statements) const square = (n) => { let result = n * n; return result; }; // Default parameters function say(msg = "Hi") { console.log(msg); } say(); // "Hi" say("Hello"); // "Hello"
this. They inherit this from the enclosing scope. Function declarations are hoisted — expressions are not.
7. Arrays & Array Methods
let arr = [1, 2, 3, 4, 5]; // Add / Remove arr.push(6); // adds to END → [1,2,3,4,5,6] arr.pop(); // removes END → [1,2,3,4,5], returns 6 arr.unshift(0); // adds to START → [0,1,2,3,4,5] arr.shift(); // removes START → [1,2,3,4,5], returns 0 // splice(start, deleteCount, ...items) — modifies original arr.splice(1, 2); // removes 2 elements from index 1 → [1,4,5] arr.splice(1, 0, 9); // inserts 9 at index 1, deletes nothing // slice(start, end) — returns new array, does NOT modify original let part = [1,2,3,4,5].slice(1, 3); // [2, 3] (index 1 up to but NOT including 3) // Search [10,20,30].indexOf(20); // 1 (index of value 20) [10,20,30].indexOf(99); // -1 (not found) [1,2,3].includes(2); // true // Iterate / Transform [1,2,3].map(x => x * 2); // [2, 4, 6] — new array, every element transformed [1,2,3,4].filter(x => x > 2); // [3, 4] — new array, only matching elements [1,2,3,4].reduce((acc, x) => acc + x, 0); // 10 — reduces to single value (sum here)
| Method | Mutates original? | Returns |
|---|---|---|
| push() | ✅ Yes | New length |
| pop() | ✅ Yes | Removed element |
| shift() | ✅ Yes | Removed element |
| unshift() | ✅ Yes | New length |
| splice() | ✅ Yes | Array of removed elements |
| slice() | ❌ No | New array (copy) |
| map() | ❌ No | New array (transformed) |
| filter() | ❌ No | New array (filtered) |
| reduce() | ❌ No | Single accumulated value |
8. String Methods
let s = " Hello, World! "; s.length // 18 (including spaces) s.trim() // "Hello, World!" — removes leading/trailing whitespace s.toUpperCase() // " HELLO, WORLD! " s.toLowerCase() // " hello, world! " s.indexOf("World") // 9 (first occurrence index, -1 if not found) s.includes("Hello") // true s.charAt(2) // "H" (character at index 2) s.substring(2, 7) // "Hello" (from index 2 up to but not including 7) s.replace("World", "JS") // " Hello, JS! " (replaces first match) s.split(",") // [" Hello", " World! "] — splits into array "hello"[0] // "h" — strings are indexable // Template literals (ES6) — backtick strings let name = "Darshan"; let msg = `Hello, ${name}! You are ${20+1} years old.`; // "Hello, Darshan! You are 21 years old."
9. Loops
// for loop for (let i = 0; i < 3; i++) { console.log(i); // 0, 1, 2 } // while loop let i = 0; while (i < 3) { console.log(i); // 0, 1, 2 i++; } // do-while — runs at LEAST once even if condition is false let j = 5; do { console.log(j); // 5 — runs once even though 5 < 3 is false } while (j < 3); // for...in — iterates over KEYS of an object (or indexes of array) const person = { name: "Ali", age: 25 }; for (let key in person) { console.log(key); // "name", "age" } // for...of — iterates over VALUES of iterable (array, string, etc.) const nums = [10, 20, 30]; for (let val of nums) { console.log(val); // 10, 20, 30 }
for...in → keys/indexes. for...of → values. Use for...of for arrays, for...in for objects.
10. Objects
// Creating an object const car = { brand: "Toyota", year: 2022, start: function() { return "Engine started!"; } }; // Accessing properties — dot notation console.log(car.brand); // "Toyota" console.log(car.year); // 2022 // Accessing properties — bracket notation (useful for dynamic keys) console.log(car["brand"]); // "Toyota" let prop = "year"; console.log(car[prop]); // 2022 // Calling a method console.log(car.start()); // "Engine started!" // Adding / modifying properties car.color = "red"; // adds new property car.year = 2023; // modifies existing // Deleting a property delete car.color;
11. DOM Manipulation
The DOM (Document Object Model) is the browser's representation of the HTML page as a tree of objects. JavaScript can read and change it.
// Selecting elements document.getElementById("myBtn") // by id — returns single element document.querySelector(".myClass") // CSS selector — returns FIRST match document.querySelectorAll("p") // CSS selector — returns ALL matches (NodeList) document.getElementsByClassName("btn") // by class — returns HTMLCollection // Changing content let el = document.getElementById("title"); el.innerHTML = "<b>New Title</b>"; // sets HTML content el.textContent = "Plain text"; // sets text only (safer — no HTML parsing) // Changing styles el.style.color = "red"; el.style.display = "none"; // Changing attributes el.setAttribute("href", "https://example.com"); el.getAttribute("href"); // Adding event listener el.addEventListener("click", function() { console.log("Clicked!"); });
12. Events
| Event | When it fires |
|---|---|
| onclick | Element is clicked |
| onchange | Value of input/select changes |
| onsubmit | Form is submitted |
| onload | Page or image finishes loading |
| onmouseover | Mouse enters element |
| onkeydown | Key is pressed down |
| onfocus | Input element gets focus |
| onblur | Input element loses focus |
// Inline HTML (older style) <button onclick="sayHi()">Click Me</button> // addEventListener (preferred) document.getElementById("btn").addEventListener("click", function(event) { event.preventDefault(); // stops default behavior (e.g., form submission) console.log("Button clicked"); });
13. Closures
A closure is when an inner function remembers variables from its outer function, even after the outer function has finished executing.
function makeCounter() { let count = 0; // this variable lives in makeCounter's scope return function() { // inner function — this is the closure count++; return count; }; } const counter = makeCounter(); // makeCounter finished, but count survives console.log(counter()); // 1 console.log(counter()); // 2 console.log(counter()); // 3 // Each call to makeCounter() creates a SEPARATE closure const counter2 = makeCounter(); console.log(counter2()); // 1 — independent from counter
14. The this Keyword
this refers to the object that is currently executing the function. Its value depends on how the function is called.
// In a method — this = the object const person = { name: "Darshan", greet() { console.log(this.name); // "Darshan" } }; person.greet(); // In a regular function (non-strict mode) — this = global (window) function show() { console.log(this); // window (browser) or global (Node.js) } // Arrow functions — NO own this, inherits from enclosing scope const obj = { name: "Test", regular: function() { console.log(this.name); // "Test" — this = obj }, arrow: () => { console.log(this.name); // undefined — arrow uses outer this (window) } };
15. Hoisting
- var declarations → hoisted to top of function, initialised as
undefined - var assignments → NOT hoisted, stay where they are
- function declarations → fully hoisted (name + body), callable anywhere
- let / const → hoisted but in Temporal Dead Zone (TDZ) — throws ReferenceError if accessed before declaration
- function expressions / arrow functions → follow the rules of the variable they're assigned to
// var hoisting console.log(x); // undefined — declaration hoisted, assignment not var x = 10; console.log(x); // 10 // Function declaration hoisting — fully hoisted sayHello(); // "Hello!" — works even before the function definition function sayHello() { console.log("Hello!"); } // let TDZ console.log(y); // ReferenceError: Cannot access 'y' before initialization let y = 5; // Function expression — NOT hoisted greet(); // TypeError: greet is not a function var greet = function() { console.log("Hi"); };
16. Truthy & Falsy Values
In JavaScript, every value is either truthy or falsy when evaluated in a boolean context (like an if statement).
false | 0 | "" (empty string) | null | undefined | NaN
Everything else is truthy — including
"0", "false", [], {}.
// Falsy — all these if-blocks do NOT run if (0) { /* skipped */ } if ("") { /* skipped */ } if (null) { /* skipped */ } if (undefined) { /* skipped */ } if (NaN) { /* skipped */ } // Truthy — these DO run if ("0") { /* runs — non-empty string, even if "0" */ } if ([]) { /* runs — empty array is truthy! */ } if ({}) { /* runs — empty object is truthy! */ } if ("false") { /* runs — non-empty string is truthy */ } if (-1) { /* runs — any non-zero number is truthy */ }
17. NaN — Not a Number
typeof NaN // "number" — NaN is paradoxically of type number isNaN(NaN) // true isNaN("abc") // true — "abc" gets coerced to NaN isNaN("123") // false — "123" coerces to number 123 isNaN(undefined) // true — undefined is NaN when converted NaN === NaN // false — NaN is NOT equal to itself! NaN == NaN // false — same rule // How to properly check for NaN: Number.isNaN(NaN) // true — stricter, doesn't coerce Number.isNaN("abc") // false — "abc" is not NaN, it's a string
NaN !== NaN is one of the most surprising facts in JavaScript. It is the only value in JS not equal to itself. Use isNaN() or Number.isNaN() to check for it.
18. JSON.parse() and JSON.stringify()
JSON (JavaScript Object Notation) is a text format for exchanging data. Two key methods convert between JSON strings and JS objects.
// JavaScript object → JSON string const user = { name: "Darshan", age: 21 }; const json = JSON.stringify(user); console.log(json); // '{"name":"Darshan","age":21}' console.log(typeof json); // "string" // JSON string → JavaScript object const str = '{"name":"Darshan","age":21}'; const obj = JSON.parse(str); console.log(obj.name); // "Darshan" console.log(typeof obj); // "object" // JSON rules — keys must be double-quoted strings // Invalid JSON: { name: "Darshan" } — key must be "name" // Valid JSON: { "name": "Darshan" }
19. setTimeout and setInterval
// setTimeout — run code ONCE after a delay (milliseconds) setTimeout(function() { console.log("Runs after 2 seconds"); }, 2000); // setTimeout with arrow function setTimeout(() => console.log("Hello!"), 1000); // setInterval — run code REPEATEDLY every N milliseconds const id = setInterval(() => { console.log("Runs every 1 second"); }, 1000); // Stop the interval clearInterval(id); // Cancel a timeout before it fires const t = setTimeout(() => console.log("Never runs"), 5000); clearTimeout(t);
20. Error Handling — try / catch / finally
try { // code that might throw an error let result = 10 / 0; JSON.parse("invalid json {"); // throws SyntaxError } catch (error) { // runs if an error is thrown inside try console.log(error.message); // "Unexpected token i in JSON..." console.log(error.name); // "SyntaxError" } finally { // ALWAYS runs — error or not console.log("Cleanup done"); } // Throw your own error function divide(a, b) { if (b === 0) throw new Error("Cannot divide by zero"); return a / b; }
finally is commonly used for cleanup (closing files, stopping loaders).
21. Browser Dialogs — prompt(), alert(), confirm()
alert("Hello!"); // Shows a message box. Returns undefined. let name = prompt("What is your name?"); // Shows input box. Returns the string entered, or null if cancelled. let confirmed = confirm("Are you sure?"); // Shows OK/Cancel dialog. Returns true (OK) or false (Cancel). console.log(typeof prompt("test")); // "string" or "object" (null)
| Function | Returns | Use for |
|---|---|---|
| alert(msg) | undefined | Showing messages |
| prompt(msg) | string or null | Getting user input |
| confirm(msg) | true or false | Yes/No decisions |
Practice Questions — Output Prediction & Concepts
console.log(1 + "2" + 3);1 + "2" hits a string so becomes "12", then "12" + 3 = "123". Left-to-right evaluation.typeof null return?typeof NaN return?console.log(0.1 + 0.2 == 0.3);0.1 + 0.2 equals 0.30000000000000004, not exactly 0.3.var x = 5; console.log(x++); console.log(x);
x++ returns the current value (5) THEN increments. Next line x is now 6.console.log(a); var a = 10;
var a is hoisted to the top and initialised as undefined. The assignment a = 10 happens later.console.log(1 == "1");console.log(1 === "1");== converts "1" to number 1 before comparing (equal). === checks type too: number ≠ string (not equal).console.log("5" - 3);- operator only does subtraction (no string concatenation). "5" is coerced to number 5, then 5 - 3 = 2.let arr = [1, 2, 3, 4]; let result = arr.filter(x => x > 2); console.log(result);
filter returns a new array with elements where the callback returns true. Only 3 and 4 are greater than 2.let arr = [1, 2, 3]; console.log(arr.map(x => x * 2)); console.log(arr);
map does NOT mutate the original array. It returns a new array. The original arr is unchanged.function makeAdder(x) { return function(y) { return x + y; }; } const add5 = makeAdder(5); console.log(add5(3));
add5 "closes over" x = 5. When called with 3, it returns 5 + 3 = 8.console.log(NaN === NaN);isNaN() or Number.isNaN() to check for NaN.let a = 10; function test() { console.log(a); var a = 20; } test();
test(), var a is hoisted within the function scope. At console.log(a), the local a exists but is undefined (not yet assigned). The outer a = 10 is shadowed.console.log(true + true + "1");true + true → 1 + 1 → 2 (both booleans convert to numbers). Then 2 + "1" → "21" (string concatenation).false, 0, "", null, undefined, NaN. Empty arrays and objects are NOT falsy.let arr = [10, 20, 30]; console.log(arr.indexOf(20)); console.log(arr.indexOf(99));
indexOf returns the index of the element (20 is at index 1). If not found, it returns -1 (99 is not in the array).console.log(typeof undefined === typeof null);typeof undefined is "undefined". typeof null is "object". "undefined" === "object" is false.let s = "Hello, World"; console.log(s.substring(7, 12));
substring(7, 12) extracts characters from index 7 up to (but not including) index 12. H(0)e(1)l(2)l(3)o(4),(5) (6)W(7)o(8)r(9)l(10)d(11) → "World".const obj = { name: "test" }; const key = "name"; console.log(obj.key); console.log(obj[key]);
obj.key looks for a property literally named "key" (not the variable) → undefined. obj[key] uses the variable's value "name" → obj["name"] → "test".let x = 10; let y = 3; console.log(x % y); console.log(x ** y);
% is modulo: 10 ÷ 3 = 3 remainder 1. ** is exponentiation: 10³ = 1000.