02 - Functions
📋 Jump to Takeaways🎁 You can call a function before you define it... but only sometimes. What decides whether that works, and why does it trip up almost everyone?
What are Functions?
Functions are reusable blocks of code that perform a specific task. They help you avoid repetition and organize your code.
// Function declaration
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
console.log(greet("Bob")); // "Hello, Bob!"Function Declaration vs Expression
// Function declaration, hoisted (can call before definition)
function add(a, b) {
return a + b;
}
// Function expression, not hoisted
const subtract = function(a, b) {
return a - b;
};
// Arrow function, modern and concise, NOT hoisted
const multiply = (a, b) => {
return a * b;
};
// Arrow function, implicit return (no braces needed for single expression)
const divide = (a, b) => a / b;Hoisting
Hoisting is JavaScript's behavior of moving declarations to the top of their scope during compilation.
✅ Function Declarations ARE Hoisted
// This works! Function declaration is hoisted
greet("Alice"); // "Hello, Alice!"
function greet(name) {
return `Hello, ${name}!`;
}❌ Function Expressions and Arrow Functions are NOT Hoisted
// ❌ Error: Cannot access 'subtract' before initialization
console.log(subtract(5, 3));
const subtract = function(a, b) {
return a - b;
};
// ❌ Error: Cannot access 'multiply' before initialization
console.log(multiply(4, 2));
const multiply = (a, b) => a * b;Why? Function expressions and arrow functions are assigned to variables declared with const or let. These variables exist in a "temporal dead zone" (TDZ), the period between entering the scope and reaching the declaration line. Accessing them during this period throws an error.
Rule of Thumb
- Want hoisting? Use function declarations
- Don't need hoisting? Use arrow functions (modern preference)
// ✅ Use function declarations for hoisting
function calculateTotal(price, tax) {
return price + (price * tax);
}
// ✅ Use arrow functions for callbacks and short functions
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);Parameters and Arguments
// Parameters, variables listed in the function definition
function introduce(name, age) {
console.log(`I'm ${name}, ${age} years old`);
}
// Arguments, actual values passed when calling
introduce("Alice", 25);
// Default parameters
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, Guest!"
console.log(greet("Alice")); // "Hello, Alice!"Return Values
// Functions can return a value
function square(n) {
return n * n;
}
let result = square(5); // 25
// Without return, function returns undefined
function logMessage(msg) {
console.log(msg);
// no return statement
}
let value = logMessage("Hi"); // undefined
// Early return
function isAdult(age) {
if (age >= 18) {
return true;
}
return false;
}
// Simpler version, return the boolean expression directly
const isAdult2 = (age) => age >= 18;Arrow Functions
Modern JavaScript prefers arrow functions for their concise syntax. Important: Arrow functions are NOT hoisted.
// Traditional function
function double(n) {
return n * 2;
}
// Arrow function, long form (explicit return)
const doubleLong = (n) => {
return n * 2;
};
// Arrow function, short form (implicit return)
const doubleShort = (n) => n * 2;
// Single parameter, parentheses optional
const doubleBare = n => n * 2;
// No parameters, parentheses required
const greet = () => "Hello!";
// Multiple parameters, parentheses required
const add = (a, b) => a + b;
console.log(doubleLong(5), doubleShort(5), doubleBare(5)); // 10 10 10
console.log(greet(), add(2, 3)); // "Hello!" 5Remember: Arrow functions must be defined before they're used:
// ❌ Error: Cannot access 'greet' before initialization
greet();
const greet = () => "Hello!";Define it first, then call it:
const greet = () => "Hello!";
greet(); // "Hello!"Scope
Variables have different visibility depending on where they're declared:
// Global scope, accessible everywhere
let global = "I'm global";
function test() {
// Function scope, only accessible inside the function
let local = "I'm local";
console.log(global); // ✅ works
console.log(local); // ✅ works
}
test();
console.log(global); // ✅ works
console.log(local); // ❌ error, local is not defined
// Block scope, let and const are block-scoped
if (true) {
let blockScoped = "I'm in the block";
var functionScoped = "I'm function-scoped";
}
console.log(functionScoped); // ✅ works (var ignores block scope)
console.log(blockScoped); // ❌ error, block-scopedCallback Functions
Functions can be passed as arguments to other functions:
// A function that takes another function as a parameter
function processUser(name, callback) {
console.log(`Processing ${name}...`);
callback(name);
}
// Pass a function as an argument
processUser("Alice", function(name) {
console.log(`Done processing ${name}`);
});
// More common with arrow functions
processUser("Bob", (name) => {
console.log(`Done processing ${name}`);
});
// Real-world example, array methods
const numbers = [1, 2, 3, 4];
numbers.forEach((num) => {
console.log(num * 2);
});Rest Parameters
Collect multiple arguments into an array:
function sum(...numbers) {
let total = 0;
for (let num of numbers) {
total += num;
}
return total;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
// Combine with regular parameters
function introduce(greeting, ...names) {
console.log(`${greeting} ${names.join(", ")}`);
}
introduce("Hello", "Alice", "Bob", "Charlie");
// "Hello Alice, Bob, Charlie"Immediately Invoked Function Expressions (IIFE)
Run a function immediately after defining it:
(function() {
console.log("I run immediately!");
})();
// With arrow function
(() => {
console.log("Also immediate!");
})();
// Useful for creating isolated scope
(function() {
let private = "Can't access me outside";
console.log(private); // works here
})();
console.log(private); // ❌ error, not definedKey Takeaways
- Functions make code reusable and organized
- Function declarations are hoisted, can be called before definition
- Arrow functions and function expressions are NOT hoisted, must be defined first
- Use arrow functions (
=>) for concise syntax (modern preference) - Always use
constorlet, nevervar - Functions can take other functions as parameters (callbacks)
- Rest parameters (
...) collect arguments into an array - Return values let functions produce results
- Scope determines where variables are accessible
🎁 Functions process values one at a time. But how do you store and work with a whole list of them, like every item in a shopping cart?