SYP Start Your Path

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!"