Module 1: Functions & Scope
Function Basics & Arrow Functions
function greet(name) {
return "Hello, " + name;
}
console.log(greet("Chiru")); // "Hello, Chiru"
// Function expression
const greet2 = function (name) {
return "Hi, " + name;
};
// Arrow function — shorter syntax, very common in modern code
const add = (a, b) => {
return a + b;
};
// Implicit return for a single expression — no braces or "return" needed
const add2 = (a, b) => a + b;
const square = x => x * x; // single param, parentheses optional
const sayHi = () => console.log("Hi!"); // no params, empty parens required
Arrow functions also handle this differently — they don't have their own
this, they inherit it from the surrounding code. This matters most inside object
methods and event handlers.
Default & Rest Parameters
// Default parameters
function greet(name = "Guest") {
console.log("Hello, " + name);
}
greet(); // "Hello, Guest"
greet("Chiru"); // "Hello, Chiru"
// Rest parameters — gathers any number of extra arguments into an array
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
Scope, Hoisting & the Temporal Dead Zone
function myFunction() {
let secret = "hidden";
console.log(secret); // fine, inside the function
}
myFunction();
console.log(secret); // ❌ Error — not visible outside (local scope)
JavaScript moves declarations to the top of their scope before running ("hoisting"), but behavior differs by keyword:
console.log(x); // undefined (not an error) — var is hoisted and initialized as undefined
var x = 5;
console.log(y); // ❌ Error: Cannot access 'y' before initialization
let y = 5; // let/const ARE hoisted, but stay in the "Temporal Dead Zone" until reached
sayHi(); // ✅ works — function declarations are fully hoisted, body included
function sayHi() {
console.log("Hi!");
}
The Temporal Dead Zone (TDZ) is the period between entering a scope and the
actual let/const declaration line, during which the variable exists but
cannot be accessed. This is a deliberate safety feature — it turns a whole class of bugs into
immediate, loud errors instead of silent undefined values.
Closures
A closure is a function that "remembers" the variables from the scope it was created in, even after that outer function has finished running:
function makeCounter() {
let count = 0; // "enclosed" by the inner function
return function () {
count++;
return count;
};
}
let counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
let counter2 = makeCounter(); // a completely independent counter
console.log(counter2()); // 1
Closures are how private state is created in JavaScript without classes.
Higher-Order Functions & Recursion
A higher-order function either accepts a function as an argument, returns a function, or both —
makeCounter above is one example. So are map, filter, and
addEventListener, both covered in later chapters.
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
repeat(3, i => console.log("Iteration", i));
Recursion is a function that calls itself, breaking a problem into smaller pieces of the same problem, until it reaches a base case that stops the recursion:
function factorial(n) {
if (n <= 1) return 1; // base case — stops the recursion
return n * factorial(n - 1); // recursive case
}
console.log(factorial(5)); // 120
function countdown(n) {
if (n <= 0) {
console.log("Done!");
return;
}
console.log(n);
countdown(n - 1);
}
countdown(3); // 3, 2, 1, "Done!"