9. Functions

Function Declaration

function greet(name) {
    return `Hello, ${name}!`;
}

console.log(greet("John")); // "Hello, John!"

Function Expression

const greet = function(name) {
    return `Hello, ${name}!`;
};

console.log(greet("Jane")); // "Hello, Jane!"

Arrow Functions (ES6+)

// Basic arrow function
const greet = (name) => `Hello, ${name}!`;

// Multiple parameters
const add = (a, b) => a + b;

// No parameters
const sayHello = () => "Hello!";

// Multiple statements (need curly braces and return)
const calculate = (a, b) => {
    let result = a + b;
    return result * 2;
};

// Returning objects
const createPerson = (name, age) => ({ name, age });

Parameters

Default Parameters (ES6+)

function greet(name = "Guest", greeting = "Hello") {
    return `${greeting}, ${name}!`;
}

console.log(greet());              // "Hello, Guest!"
console.log(greet("John"));        // "Hello, John!"
console.log(greet("John", "Hi"));  // "Hi, John!"

Rest Parameters (ES6+)

function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3));       // 6
console.log(sum(1, 2, 3, 4, 5)); // 15

Destructuring Parameters

function printUser({ name, age, city = "Unknown" }) {
    console.log(`${name} is ${age} years old and lives in ${city}.`);
}

const user = { name: "John", age: 30 };
printUser(user); // "John is 30 years old and lives in Unknown."

Function Scope and Closures

Function Scope

function outer() {
    let outerVar = "I'm outer";

    function inner() {
        let innerVar = "I'm inner";
        console.log(outerVar); // Can access outer scope
        console.log(innerVar); // Can access own scope
    }

    inner();
    // console.log(innerVar); // Error: innerVar not accessible
}

outer();

Closures

function createCounter() {
    let count = 0;

    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

Higher-Order Functions

Functions that take other functions as parameters or return functions:

function applyOperation(a, b, operation) {
    return operation(a, b);
}

const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

console.log(applyOperation(5, 3, add));      // 8
console.log(applyOperation(5, 3, multiply)); // 15

Immediately Invoked Function Expressions (IIFE)

// Traditional IIFE
(function() {
    console.log("This runs immediately!");
})();

// Arrow function IIFE
(() => {
    console.log("Arrow IIFE!");
})();

Function Methods

call(), apply(), bind()

function greet(greeting, punctuation) {
    return `${greeting}, ${this.name}${punctuation}`;
}

const person = { name: "John" };

// call() - passes arguments individually
console.log(greet.call(person, "Hello", "!")); // "Hello, John!"

// apply() - passes arguments as array
console.log(greet.apply(person, ["Hi", "."])); // "Hi, John."

// bind() - creates new function with bound context
const boundGreet = greet.bind(person, "Hey", "!");
console.log(boundGreet()); // "Hey, John!"

Recursive Functions

function factorial(n) {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

console.log(factorial(5)); // 120

// Tail recursion (optimized in some engines)
function factorialTail(n, accumulator = 1) {
    if (n <= 1) {
        return accumulator;
    }
    return factorialTail(n - 1, n * accumulator);
}

Generator Functions (ES6+)

function* numberGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

const generator = numberGenerator();

console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }

Async Functions (ES8+)

async function fetchUserData(userId) {
    try {
        const response = await fetch(`/api/users/${userId}`);
        const userData = await response.json();
        return userData;
    } catch (error) {
        console.error("Error fetching user data:", error);
    }
}

// Usage
fetchUserData(123).then(user => console.log(user));

Function Properties

function myFunction(a, b, c) {
    // Function body
}

console.log(myFunction.length);    // 3 (number of parameters)
console.log(myFunction.name);      // "myFunction"

Next Steps

Functions are fundamental to JavaScript programming. Next, let's explore objects and prototypes, the building blocks of object-oriented programming in ECMAScript.