Notifications

No notifications

/Phase 2

JS Fundamentals

JavaScript — The Language of the Web

JavaScript is the programming language of the browser (and beyond, via Node.js). Mastering variables, types, operators, control flow, functions, and scope is the foundation for everything.

Variables

KeywordScopeReassignableHoisted
varFunction✅ (as undefined)
letBlock❌ (TDZ)
constBlock❌ (TDZ)

Rule: Default to const. Use let only when you need to reassign. Avoid var.

Data Types

PrimitiveExampletypeof
String"hello""string"
Number42, 3.14"number"
Booleantrue"boolean"
undefinedundefined"undefined"
nullnull"object" (bug)
BigInt9007199254740993n"bigint"
SymbolSymbol("id")"symbol"

Non-primitives: Object, Array, Function (all reference types).

Operators & Equality

// Always use strict equality
5 === 5    // true  (same type + value)
5 == "5"   // true  (coerces type — avoid!)
5 === "5"  // false (different types)

Control Flow

if (condition) { … }
else if (other) { … }
else { … }

for (let i = 0; i < arr.length; i++) { … } for (const item of arr) { … } // iterate values for (const key in obj) { … } // iterate keys

while (condition) { … } switch (value) { case 'a': …; break; }

Functions & Scope

Functions are first-class — they can be assigned to variables, passed as arguments, and returned from other functions. Each function creates its own scope.

On this page

Detailed Theory

What JavaScript Actually Does

HTML is the *structure* of a page, CSS is the *look*. JavaScript is the behaviour — the language that makes a button do something when you click it, fetches data from a server, or animates an element. It's the only programming language that runs natively in every browser.

If you can read a recipe, you can read JavaScript: it's a list of instructions the browser executes top to bottom.

Running Your First Line

Open any web page, press F12, click Console, and type:

console.log("Hello, world!");

That's it — you're a JavaScript developer. console.log prints a value into that console panel. You'll use it constantly while debugging.

Variables — Storing Things by Name

let age = 21;          // can change later
const name = "Asha";   // cannot change

KeywordReassignable?Use it for
constNoAlmost everything (default)
letYesCounters, accumulators, anything you'll reassign
varYesAvoid — old, function-scoped, surprising

Rule of thumb: start with const. Only switch to let if the code refuses to compile because you really need to reassign.

Data Types in 30 Seconds

TypeExample
String"hello" or 'hi' or \bye\``
Number42, 3.14 (no separate int/float)
Booleantrue, false
nullnull (intentional empty)
undefinedundefined (variable declared but no value)
Object{ name: "Asha", age: 21 }
Array[1, 2, 3] (an object too, technically)

Use typeof value to check at runtime.

Strings — Template Literals Are Your Friend

Old way: "Hello, " + name + "!"

New way (backticks):

const greeting = Hello, ${name}! You are ${age}.;

Backticks also support multi-line strings without \n.

Operators — The Bits That Actually Compute

// Arithmetic
1 + 2; 5 * 3; 10 / 4; 10 % 3;  // remainder = 1
2 ** 10;                        // exponent = 1024

// Comparison — ALWAYS prefer === over == 5 === "5"; // false (strict, types matter) 5 == "5"; // true (loose, coerces — avoid)

// Logical true && false; // and true || false; // or !true; // not

Control Flow

if (age >= 18) {
  console.log("adult");
} else if (age >= 13) {
  console.log("teen");
} else {
  console.log("kid");
}

// Ternary — a one-line if/else const label = age >= 18 ? "adult" : "minor";

// Loops for (let i = 0; i < 5; i++) console.log(i);

const fruits = ["apple", "banana", "cherry"]; for (const fruit of fruits) console.log(fruit);

Functions — Reusable Recipes

Three ways to define a function — pick whichever feels natural at first:

// 1. Function declaration
function add(a, b) { return a + b; }

// 2. Function expression const subtract = function (a, b) { return a - b; };

// 3. Arrow function (modern, concise) const multiply = (a, b) => a * b; const square = n => n * n; // single param, no parens const greet = name => { console.log(Hi, ${name}); };

Arrow functions are the most common in modern code. Use function declarations when you want hoisting (more on that below).

Arrays — Lists of Stuff

const nums = [10, 20, 30];

nums.length; // 3 nums[0]; // 10 nums.push(40); // add to end nums.pop(); // remove last nums.includes(20); // true

// Modern iteration — learn these three before everything else: nums.map(n => n * 2); // [20, 40, 60] nums.filter(n => n > 15); // [20, 30] nums.reduce((sum, n) => sum + n, 0); // 60

Objects — Things with Properties

const user = { name: "Asha", age: 21, email: "a@x.com" };

user.name; // "Asha" user["age"]; // 21 user.role = "admin"; // add a property

// Destructuring — pull fields out cleanly const { name, age } = user;

// Spread — copy + extend const updated = { ...user, age: 22 };

Beginner Mistakes to Skip

1. Using == instead of === — gives wrong answers like 0 == ""true. 2. Forgetting return in a function — it silently returns undefined. 3. Mutating a const objectconst user = {} only locks the *binding*; you can still change properties. Use Object.freeze if you really need immutability. 4. Looping with for...in over arrays — iterates indices and inherited keys. Use for...of or .forEach instead. 5. Re-declaring with var inside a block and being shocked it leaks out.

Intermediate: Lexical Scope

JavaScript uses lexical (static) scope — a function can access variables from its enclosing scope based on where it's *written*, not where it's called.

function outer() {
  const x = 10;
  function inner() {
    console.log(x); // 10 — sees outer's x
  }
  inner();
}

let and const are block-scoped (live only inside the nearest { }); var is function-scoped (leaks out of blocks). One more reason to skip var.

Intermediate: Closures

A closure is a function that remembers the variables of the scope where it was created — even after that scope has ended.

function makeCounter() {
  let count = 0;
  return () => ++count;
}

const counter = makeCounter(); counter(); // 1 counter(); // 2 — count is alive inside the closure

Closures power every callback, event handler, and module pattern in JavaScript. They also enable data privacy:

function makeWallet(initial) {
  let balance = initial; // private — no outside access
  return {
    deposit: (n) => { balance += n; },
    getBalance: () => balance,
  };
}

Intermediate: Hoisting & The Temporal Dead Zone

JavaScript "hoists" declarations to the top of their scope before running the code:

DeclarationHoisted?Initialised?
function declarationYesYes (full body)
varYesAs undefined
let / constYesNo — touching them throws ReferenceError (the *Temporal Dead Zone*)
Arrow functions / function expressionsNo

console.log(x); // undefined  (hoisted var)
console.log(y); // ReferenceError (TDZ)
var x = 1;
let y = 2;

Intermediate: Type Coercion Traps

JavaScript will silently convert types around you:

"5" + 3       // "53"  — + with a string = concat
"5" - 3       //   2   — - forces numeric
null + 1      //   1
undefined + 1 //  NaN
[] + []       //  ""
[] + {}       //  "[object Object]"

Defence: always use ===, and convert explicitly with Number(x), String(x), Boolean(x).

Advanced: this Without the Headache

In a regular function, this depends on how it's called:

function show() { console.log(this); }
show();              // window / undefined (strict)
const obj = { show };
obj.show();          // obj — called as a method

In an arrow function, this is whatever this was in the *surrounding* code — it never has its own. That's exactly why arrows are perfect for callbacks inside class methods.

Advanced: The Event Loop in One Picture

JavaScript runs on a single thread but feels async because of the event loop:

Call Stack  ──────► runs sync code
   │
   │ schedules async work
   ▼
Web APIs (timers, fetch, DOM events)
   │ when ready
   ▼
Microtask queue (promises) ── higher priority
Macrotask queue (setTimeout, etc.)
   │
   ▼ event loop pops one task when stack is empty

Why does Promise.resolve().then(…) always run before setTimeout(…, 0)? Microtasks drain *before* the next macrotask. Knowing this saves hours of head-scratching.

Advanced: Modules

Modern JavaScript splits code across files using import/export:

// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }

// app.js import { PI, add } from "./math.js";

In the browser, add