Skip to main content

Module 5: Functions

Functions are the fundamental building blocks of JavaScript. They allow you to write reusable, modular, and maintainable code.


1. Function Declaration

The traditional way to define a function.

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

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

Characteristics

  • Hoisted – can be called before declaration
  • Has this binding
  • Has arguments object
// Works due to hoisting
sayHi(); // Hello!

function sayHi() {
console.log('Hello!');
}

2. Function Expression

Function assigned to a variable.

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

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

Named Function Expression

const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // Can reference itself
};
Hoisting Difference

Function expressions are not hoisted. They must be defined before use.

sayHi(); // ❌ Error: Cannot access before initialization
const sayHi = function() {
console.log('Hello!');
};

3. Arrow Functions (ES6)

Modern, concise syntax for functions.

Basic Syntax

// Traditional
function add(a, b) {
return a + b;
}

// Arrow function
const add = (a, b) => {
return a + b;
};

// Concise (implicit return)
const add = (a, b) => a + b;

// Single parameter (parentheses optional)
const square = x => x * x;

// No parameters
const greet = () => 'Hello!';

Important Differences

// No 'this' binding (lexical this)
// No 'arguments' object
// Cannot be used as constructors
// Cannot be generators
When to Use Arrow Functions

Use arrow functions for:

  • Short, simple functions
  • Callbacks and array methods
  • When you need lexical this

Use regular functions for:

  • Methods in objects/classes
  • Functions needing arguments
  • Constructor functions

4. Parameters and Arguments

4.1 Default Parameters (ES6)

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

greet(); // Hello, Guest!
greet('John'); // Hello, John!
greet('Jane', 'Hi'); // Hi, Jane!

4.2 Rest Parameters (ES6)

Collect all remaining arguments into an array.

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

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

// Rest must be last parameter
function logInfo(message, ...details) {
console.log(message);
console.log(details);
}

4.3 Spread Operator

const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 3

// Combining arrays
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]

4.4 Destructuring Parameters

// Object destructuring
function createUser({ name, age, email }) {
return { name, age, email };
}

createUser({ name: 'John', age: 30, email: 'john@example.com' });

// Array destructuring
function getCoordinates([x, y]) {
return { x, y };
}

getCoordinates([10, 20]);

// With defaults
function login({ username, password, remember = false }) {
// ...
}

5. Return Statement

5.1 Explicit Return

function add(a, b) {
return a + b;
}

function getUser(id) {
const user = findUser(id);
return user;
}

5.2 Implicit Return (Arrow Functions)

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

const getUser = id => ({ id, name: 'John' }); // Object needs parentheses

5.3 Early Return

function processOrder(order) {
if (!order) return null;
if (!order.items.length) return null;

// Process order
return processedOrder;
}

5.4 Multiple Return Values

// Return object
function getUser() {
return {
name: 'John',
age: 30,
email: 'john@example.com'
};
}

// Return array (for destructuring)
function getCoordinates() {
return [10, 20];
}

const [x, y] = getCoordinates();

6. Scope

6.1 Global Scope

const globalVar = 'I am global';

function test() {
console.log(globalVar); // Accessible
}

6.2 Function Scope

function test() {
var functionScoped = 'I am function scoped';
console.log(functionScoped); // ✅ Works
}

console.log(functionScoped); // ❌ Error

6.3 Block Scope (let & const)

{
let blockScoped = 'I am block scoped';
const alsoBlockScoped = 'Me too';
console.log(blockScoped); // ✅ Works
}

console.log(blockScoped); // ❌ Error

6.4 Lexical Scope

function outer() {
const outerVar = 'outer';

function inner() {
console.log(outerVar); // ✅ Can access parent scope
}

inner();
}

7. Closures

A closure is a function that has access to variables in its outer scope, even after the outer function has returned.

Basic Closure

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

Practical Use Cases

// Private variables
function createBankAccount(initialBalance) {
let balance = initialBalance;

return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
return balance;
}
return 'Insufficient funds';
},
getBalance() {
return balance;
}
};
}

const account = createBankAccount(1000);
account.deposit(500); // 1500
account.withdraw(200); // 1300
// Cannot access balance directly

Function Factory

function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15
Closure Memory

Closures keep references to outer variables, which can prevent garbage collection. Be mindful in loops and large-scale applications.


8. Higher-Order Functions

Functions that accept functions as parameters or return functions.

Functions as Arguments

function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}

repeat(3, console.log);
// 0
// 1
// 2

// Callback pattern
function fetchData(url, callback) {
// Simulate async operation
setTimeout(() => {
const data = { user: 'John' };
callback(data);
}, 1000);
}

fetchData('/api/user', data => {
console.log(data);
});

Functions as Return Values

function createGreeter(greeting) {
return function(name) {
return `${greeting}, ${name}!`;
};
}

const sayHello = createGreeter('Hello');
const sayHi = createGreeter('Hi');

console.log(sayHello('John')); // Hello, John!
console.log(sayHi('Jane')); // Hi, Jane!

9. Recursion

A function that calls itself.

Basic Recursion

function countdown(n) {
if (n <= 0) return; // Base case
console.log(n);
countdown(n - 1); // Recursive call
}

countdown(5); // 5, 4, 3, 2, 1

Factorial

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

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

Fibonacci

function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}

// With memoization (optimization)
function fibonacciMemo() {
const cache = {};

return function fib(n) {
if (n in cache) return cache[n];
if (n <= 1) return n;

cache[n] = fib(n - 1) + fib(n - 2);
return cache[n];
};
}

const fib = fibonacciMemo();
console.log(fib(10)); // 55
Stack Overflow

Deep recursion can cause stack overflow. Consider iteration or tail call optimization when possible.


10. Immediately Invoked Function Expression (IIFE)

Function that executes immediately after definition.

(function() {
console.log('I run immediately!');
})();

// With parameters
(function(name) {
console.log(`Hello, ${name}!`);
})('John');

// Arrow function IIFE
(() => {
console.log('Modern IIFE');
})();

// Use case: Private scope
const counter = (function() {
let count = 0;

return {
increment: () => ++count,
decrement: () => --count,
value: () => count
};
})();

11. Function Methods

11.1 call()

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

const user = { name: 'John' };
console.log(greet.call(user, 'Hello', '!')); // Hello, John!

11.2 apply()

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

const user = { name: 'John' };
console.log(greet.apply(user, ['Hello', '!'])); // Hello, John!

11.3 bind()

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

const user = { name: 'John' };
const greetJohn = greet.bind(user);

console.log(greetJohn('Hello')); // Hello, John!
console.log(greetJohn('Hi')); // Hi, John!

// Partial application
const sayHello = greet.bind(user, 'Hello');
console.log(sayHello()); // Hello, John!

12. Best Practices

12.1 Keep Functions Small

// Bad: Does too much
function processUser(user) {
// Validate
// Transform
// Save to database
// Send email
// Log activity
}

// Good: Single responsibility
function validateUser(user) { /* ... */ }
function transformUser(user) { /* ... */ }
function saveUser(user) { /* ... */ }
function sendWelcomeEmail(user) { /* ... */ }

12.2 Use Descriptive Names

// Bad
function f(x) { return x * 2; }

// Good
function doubleNumber(number) { return number * 2; }

12.3 Avoid Side Effects

// Bad: Modifies external state
let total = 0;
function addToTotal(num) {
total += num;
}

// Good: Pure function
function add(a, b) {
return a + b;
}

12.4 Prefer Pure Functions

// Pure: Same input = same output, no side effects
function calculateTax(amount, rate) {
return amount * rate;
}

// Impure: Depends on external state
const taxRate = 0.1;
function calculateTax(amount) {
return amount * taxRate;
}

Summary

In this module, you learned:

  • ✅ Function declarations, expressions, and arrow functions
  • ✅ Parameters: default, rest, spread, destructuring
  • ✅ Return statements and patterns
  • ✅ Scope: global, function, block, lexical
  • ✅ Closures and their practical applications
  • ✅ Higher-order functions
  • ✅ Recursion and memoization
  • ✅ IIFE and function methods
  • ✅ Best practices for clean functions
Next Steps

In Module 6, you'll learn about Arrays – one of the most important data structures in JavaScript.


Practice Exercises

  1. Create a function with default parameters
  2. Write a function using rest parameters to sum any number of arguments
  3. Implement a counter using closures
  4. Create a higher-order function that filters an array
  5. Write a recursive function to reverse a string
  6. Build a function factory that creates customized calculators
  7. Implement memoization for an expensive function
  8. Create a utility library using IIFE

Additional Resources