Language lab · Beginner → Web
🟨 JavaScript
Concept → trace → write code → run tests → explain why → improve → mini-project. Start in the browser if you like; set up your own computer when you are ready.
📖 Topics with real code (16)
Every verified output below was produced by running the code with v22.23.4 on 5 Nov 2026. Open a topic to edit the code and run it right here in your browser.
🌱 Beginner (5)
Variables and data types
let can change, const cannot be reassigned. JavaScript types: number (int and decimal together), boolean, string, null, undefined, bigint and object. typeof shows the type.
let age = 15;
const height = 1.62;
const isStudent = true;
const name = "Asha";
console.log(name, age, height, isStudent);
console.log(typeof age, typeof isStudent, typeof name, typeof null, typeof undefined, typeof 10n);
console.log(Number("88") + 2, String(90) + "!", parseInt("42px"), 0.1 + 0.2);
console.log(Number.isInteger(5), Number.isInteger(5.5), Number.MAX_SAFE_INTEGER);
✅ Verified output
Asha 15 1.62 true number boolean string object undefined bigint 90 90! 42 0.30000000000000004 true false 9007199254740991
Operators and boolean logic
Use === (strict equal) instead of ==, which converts types. && || ! are the logical operators; ?? gives a default only for null or undefined.
const a = 17, b = 5;
console.log(a + b, a - b, a * b, a / b, Math.floor(a / b), a % b, a ** 2);
console.log(5 == "5", 5 === "5", 0 == false, 0 === false);
const hasTicket = true, isVip = false;
console.log("enter:", hasTicket && !isVip, "| either:", hasTicket || isVip);
const nickname = null;
console.log(nickname ?? "no nickname", 0 || "zero is falsy", 0 ?? "kept");
✅ Verified output
22 12 85 3.4 3 2 289 true false true false enter: true | either: true no nickname zero is falsy 0
Conditionals: if, else, switch, ternary
if/else chooses a branch, switch compares one value with many cases, and the ternary a ? b : c is a short if/else that gives a value.
const marks = 78;
let grade;
if (marks >= 90) grade = "A+";
else if (marks >= 75) grade = "A";
else if (marks >= 35) grade = "Pass";
else grade = "Fail";
console.log("Grade:", grade);
const day = "sat";
switch (day) {
case "sat":
case "sun":
console.log("Weekend");
break;
default:
console.log("School day");
}
console.log(marks >= 35 ? "passed" : "try again");
✅ Verified output
Grade: A Weekend passed
Loops: for, while, for...of, break, continue
for counts, while repeats on a condition, for...of walks arrays and strings. break stops and continue skips one turn.
for (let i = 1; i <= 5; i++) {
if (i === 3) continue;
console.log("for", i);
}
let n = 10;
while (n > 0) {
if (n < 4) break;
console.log("while", n);
n -= 3;
}
for (const ch of "abc") console.log("char", ch);
for (let r = 1; r <= 3; r++) {
let row = "";
for (let c = 1; c <= 3; c++) row += String(r * c).padStart(3);
console.log(row);
}
✅ Verified output
for 1 for 2 for 4 for 5 while 10 while 7 while 4 char a char b char c 1 2 3 2 4 6 3 6 9
Functions, arrow functions and scope
Functions can have default parameters and rest parameters. Arrow functions are a shorter syntax. Variables declared with let/const live only inside their block.
function area(length, width = 1) {
return length * width;
}
const greet = (name, greeting = "Namaste") => `${greeting}, ${name}!`;
const stats = (...nums) => [Math.min(...nums), Math.max(...nums), nums.reduce((s, x) => s + x, 0) / nums.length];
console.log(area(5, 3), area(7));
console.log(greet("Ravi"), greet("Meena", "Hello"));
const [low, high, avg] = stats(4, 9, 2, 7);
console.log(low, high, avg);
{
const inside = "block scope";
console.log(inside);
}
console.log(typeof inside);
✅ Verified output
15 7 Namaste, Ravi! Hello, Meena! 2 9 5.5 block scope undefined
🌿 Intermediate (7)
Arrays
Arrays are ordered lists. push adds, sort orders (give a compare function for numbers!), and map/filter/reduce transform them without loops.
const marks = [72, 95, 64, 88];
marks.push(91);
marks.sort((x, y) => x - y);
console.log(marks, "first:", marks[0], "last:", marks.at(-1), "top 2:", marks.slice(-2));
console.log([10, 9, 1].sort(), "<- default sort compares text!");
const oddSquares = [1, 2, 3, 4, 5].filter((x) => x % 2).map((x) => x * x);
console.log("odd squares:", oddSquares, "sum:", oddSquares.reduce((s, x) => s + x, 0));
const [first, ...rest] = marks;
console.log(first, rest);
✅ Verified output
[ 64, 72, 88, 91, 95 ] first: 64 last: 95 top 2: [ 91, 95 ] [ 1, 10, 9 ] <- default sort compares text! odd squares: [ 1, 9, 25 ] sum: 35 64 [ 72, 88, 91, 95 ]
Strings and template literals
Strings are immutable. Template literals use backticks and ${} for values. Useful methods: trim, toUpperCase, split, join, replace, includes, padStart.
const s = " GayaGnan learning ";
const t = s.trim();
console.log(t.toUpperCase(), "|", t.replace("learning", "labs"), "|", t.length);
const words = t.split(" ");
console.log(words, "->", words.join("-"));
const price = 49.5, qty = 3;
console.log(`Total: ₹${(price * qty).toLocaleString("en-IN", { minimumFractionDigits: 2 })} for ${qty} items`);
console.log("palindrome:", "level" === [..."level"].reverse().join(""), t.includes("Gnan"));
✅ Verified output
GAYAGNAN LEARNING | GayaGnan labs | 17 [ 'GayaGnan', 'learning' ] -> GayaGnan-learning Total: ₹148.50 for 3 items palindrome: true true
Objects, Map and Set
Plain objects hold named properties. Map is a key-value store with any key type; Set keeps unique values.
const stock = { pen: 40, book: 12 };
stock.eraser = 25;
stock.pen -= 5;
for (const [item, count] of Object.entries(stock).sort()) console.log(item, count);
console.log("has ruler?", "ruler" in stock);
const a = new Set(["maths", "science", "art"]), b = new Set(["science", "music"]);
console.log("both:", [...a].filter((x) => b.has(x)), "| either:", [...new Set([...a, ...b])].sort());
const freq = new Map();
for (const w of "the cat and the hat and the bat".split(" ")) freq.set(w, (freq.get(w) || 0) + 1);
console.log(Object.fromEntries(freq));
✅ Verified output
book 12
eraser 25
pen 35
has ruler? false
both: [ 'science' ] | either: [ 'art', 'maths', 'music', 'science' ]
{ the: 3, cat: 1, and: 2, hat: 1, bat: 1 }
Classes and private fields
class bundles data and methods. Fields starting with # are truly private: code outside the class cannot read them.
class BankAccount {
#balance;
constructor(owner, balance = 0) {
this.owner = owner;
this.#balance = balance;
}
deposit(amount) {
if (amount <= 0) throw new RangeError("amount must be positive");
this.#balance += amount;
}
get balance() {
return this.#balance;
}
}
const acc = new BankAccount("Asha", 500);
acc.deposit(250);
console.log(acc.owner, acc.balance, Object.keys(acc));
✅ Verified output
Asha 750 [ 'owner' ]
Inheritance and polymorphism
extends makes a subclass; super calls the parent. Each subclass overrides area(), and the same loop works for all shapes.
class Shape {
area() { throw new Error("not implemented"); }
toString() { return `${this.constructor.name} with area ${this.area().toFixed(2)}`; }
}
class Circle extends Shape {
constructor(r) { super(); this.r = r; }
area() { return Math.PI * this.r ** 2; }
}
class Rect extends Shape {
constructor(w, h) { super(); this.w = w; this.h = h; }
area() { return this.w * this.h; }
}
for (const s of [new Circle(1), new Rect(3, 4)]) console.log(String(s), s instanceof Shape);
✅ Verified output
Circle with area 3.14 true Rect with area 12.00 true
Errors and exceptions
try/catch/finally handles errors. throw raises one; custom error classes extend Error. JSON.parse throws on bad input.
function safeParse(text) {
try {
return JSON.parse(text);
} catch (e) {
console.log("bad JSON:", e.name);
return null;
} finally {
console.log("checked", text);
}
}
console.log(safeParse('{"marks": 88}'));
console.log(safeParse("{marks: 88}"));
class ValidationError extends Error {}
try {
throw new ValidationError("age must be 0-120");
} catch (e) {
console.log(e instanceof ValidationError, e.message);
}
✅ Verified output
checked {"marks": 88}
{ marks: 88 }
bad JSON: SyntaxError
checked {marks: 88}
null
true age must be 0-120
Files with Node.js
In Node.js the fs module reads and writes files (browsers cannot touch your disk). Here we write a CSV to the temp folder and read it back.
const fs = require("fs"), os = require("os"), path = require("path");
const file = path.join(os.tmpdir(), "marks.csv");
fs.writeFileSync(file, "name,marks\nAsha,88\nRavi,95\n");
const rows = fs.readFileSync(file, "utf8").trim().split("\n").slice(1).map((l) => {
const [name, marks] = l.split(",");
return { name, marks: Number(marks) };
});
console.log(rows);
console.log("average:", rows.reduce((s, r) => s + r.marks, 0) / rows.length);
fs.unlinkSync(file);
✅ Verified output
[ { name: 'Asha', marks: 88 }, { name: 'Ravi', marks: 95 } ]
average: 91.5
🌳 Advanced (4)
Recursion and memoization
A recursive function solves a smaller version of itself. A Map cache (memoization) makes fib(80) instant; BigInt keeps the large answer exact.
const memo = new Map();
function fib(n) {
if (n < 2) return BigInt(n);
if (memo.has(n)) return memo.get(n);
const v = fib(n - 1) + fib(n - 2);
memo.set(n, v);
return v;
}
function binarySearch(a, target, lo = 0, hi = a.length - 1) {
if (lo > hi) return -1;
const mid = (lo + hi) >> 1;
if (a[mid] === target) return mid;
return a[mid] < target ? binarySearch(a, target, mid + 1, hi) : binarySearch(a, target, lo, mid - 1);
}
console.log(Array.from({ length: 10 }, (_, i) => Number(fib(i))), String(fib(80)));
console.log(binarySearch([2, 5, 8, 12, 16, 23, 38], 23));
✅ Verified output
[ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ] 23416728348467685 5
Higher-order functions and closures
Functions can return functions. A closure remembers variables from where it was made: here each counter keeps its own count.
const applyTwice = (f, x) => f(f(x));
function makeCounter(step = 1) {
let count = 0;
return () => (count += step);
}
const byOne = makeCounter(), byTen = makeCounter(10);
byOne(); byOne();
console.log(byOne(), byTen(), byTen());
const students = [["Asha", 88], ["Ravi", 95], ["Meena", 72]];
console.log(students.toSorted ? "toSorted available" : "sort copy", [...students].sort((a, b) => b[1] - a[1]));
console.log(applyTwice((n) => n * 3, 2), applyTwice((s) => s + "!", "hi"));
const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
console.log(compose((x) => x + 1, (x) => x * 2)(5));
✅ Verified output
3 10 20 toSorted available [ [ 'Ravi', 95 ], [ 'Asha', 88 ], [ 'Meena', 72 ] ] 18 hi!! 11
Asynchronous code: promises and async/await
JavaScript runs one thing at a time but does not wait for timers or network. Promise.all runs the waits together, so three 200 ms tasks take about 200 ms.
const wait = (ms, v) => new Promise((ok) => setTimeout(() => ok(v), ms));
async function main() {
const t = Date.now();
const pages = await Promise.all([wait(200, "page0"), wait(200, "page1"), wait(200, "page2")]);
console.log("all:", pages, "fast:", Date.now() - t < 400);
const first = await Promise.race([wait(300, "slow"), wait(100, "quick")]);
console.log("race:", first);
try {
await Promise.reject(new Error("network down"));
} catch (e) {
console.log("caught:", e.message);
}
}
console.log("before main finishes");
main();
✅ Verified output
before main finishes all: [ 'page0', 'page1', 'page2' ] fast: true race: quick caught: network down
References, copying and immutability
Objects and arrays are shared by reference. Spread makes a shallow copy; structuredClone makes a deep copy; Object.freeze stops changes.
const a = { name: "Asha", marks: [88, 92] };
const b = a;
const c = { ...a };
const d = structuredClone(a);
a.marks.push(75);
a.name = "Asha R";
console.log("b", b.name, b.marks, b === a);
console.log("c", c.name, c.marks);
console.log("d", d.name, d.marks);
const frozen = Object.freeze({ pi: 3.14 });
frozen.pi = 3;
console.log("frozen:", frozen.pi, Object.isFrozen(frozen));
✅ Verified output
b Asha R [ 88, 92, 75 ] true c Asha [ 88, 92, 75 ] d Asha [ 88, 92 ] frozen: 3.14 true
🧪 Browser workspace
Runs in a sandbox inside your browser with a time limit: no install needed, and your code never reaches our server.
Loading the workspace…
🛠️ Set up and verify
- Install
winget install OpenJS.NodeJS.LTS
brew install node
sudo apt install nodejs npm
- Editor: Visual Studio Code with ESLint, Prettier.
- Create
hello.jsconsole.log('Hello, GayaGnan!'); - Run it and check the version
node hello.js node --version
- Health check: paste what your terminal printed.
Install one language at a time. These commands only install or print versions; nothing here deletes files. Versions change: check the official page and note the version you installed.
🧭 The 14-lab path
- 00 · Setup and verify — install the runtime and editor, run hello world, print the version, fix PATH
- 01 · Syntax and trace — follow each line and the value of every variable
- 02 · Logic — conditionals and decision trees
- 03 · Loops — trace tables and loop challenges
- 04 · Functions — break problems down and test each piece
- 05 · Data structures — lists, maps, stacks, queues and trees you can see
- 06 · Debugging — bug cases with a hint ladder
- 07 · Algorithms — search, sort and Big-O comparison
- 08 · Files and data — read and write fixture files
- 09 · API or database — talk to a mock API or a sandbox database 🧪 REST Request & Response
- 10 · Testing — write tests and read failing assertions
- 11 · Security — secure coding and dependency hygiene for this language 🧪 Authentication with Fake Identities
- 12 · Project — one portfolio mini-project
- 13 · Assessment — quiz, coding test, rubric and an explain-your-answer prompt
📚 Modules
- variables and types
- operators
- conditionals
- loops
- functions
- arrays and objects
- DOM
- events
- modules
- promises
- async/await
- fetch with mock APIs
- error handling
- Node CLI
- unit tests (Vitest/Jest)
- security and accessibility basics
🏗️ Mini-projects
- Interactive quiz
- Calculator
- To-do app
- Accessible dashboard
- Mock API client
- Simple Node REST API
Projects use synthetic data only.