Introduction
Functions are the building blocks of JavaScript applications. They allow you to write reusable code, organize logic, and create powerful abstractions. In this lesson, you'll master function syntax, understand scope, and learn about closures.
Learning Objectives
By the end of this lesson, you will be able to:
- Write functions using both traditional and arrow function syntax
- Understand the differences between function declarations and expressions
- Master scope concepts (global, function, and block scope)
- Create and use closures for data privacy and encapsulation
- Work with higher-order functions and callbacks
- Use array methods like map, filter, and reduce effectively
Prerequisites
- Completion of Lesson 01 (JavaScript Fundamentals)
- Understanding of variables and data types
- Basic knowledge of arrays and objects
Estimated Time
3.5 hours (including practice exercises)
Function Declarations
The traditional way to define functions in JavaScript:
// Basic function declaration
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
// Function with multiple parameters
function add(a, b) {
return a + b;
}
console.log(add(5, 3)); // 8
// Function with default parameters
function createUser(name, role = "user") {
return { name, role };
}
console.log(createUser("Bob"));
// { name: "Bob", role: "user" }
Arrow Functions
Modern ES6 syntax for writing concise functions:
// Traditional function
function double(x) {
return x * 2;
}
// Arrow function (concise)
const doubleArrow = (x) => x * 2;
// Arrow function with multiple parameters
const multiply = (a, b) => a * b;
// Arrow function with block body
const processUser = (user) => {
const fullName = `${user.first} ${user.last}`;
return { ...user, fullName };
};
// Single parameter (parentheses optional)
const square = x => x * x;
console.log(square(5)); // 25
this value, making them ideal for callbacks and array methods.
Scope
Scope determines where variables are accessible in your code:
Global Scope
// Global variable (accessible everywhere)
const globalVar = "I'm global";
function showGlobal() {
console.log(globalVar); // Accessible
}
showGlobal(); // "I'm global"
Function Scope
function myFunction() {
const localVar = "I'm local";
console.log(localVar); // Accessible
}
myFunction();
// console.log(localVar); // Error: localVar is not defined
Block Scope
if (true) {
const blockVar = "I'm in a block";
let anotherBlockVar = "Me too";
console.log(blockVar); // Accessible
}
// console.log(blockVar); // Error: not accessible outside block
// var doesn't respect block scope (another reason to avoid it)
if (true) {
var oldStyle = "I leak out";
}
console.log(oldStyle); // "I leak out" (not recommended)
Closures
A closure is a function that has access to variables from its outer scope, even after the outer function has returned:
// Basic closure example
function createCounter() {
let count = 0; // Private variable
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// count is not accessible directly
// console.log(count); // Error
Practical Closure Example
// Create a private data store
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(100);
console.log(account.deposit(50)); // 150
console.log(account.withdraw(30)); // 120
console.log(account.getBalance()); // 120
// console.log(account.balance); // undefined (private)
Higher-Order Functions
Functions that take other functions as arguments or return functions:
// Function that takes a function as argument
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
repeat(3, (i) => console.log(`Iteration ${i}`));
// Iteration 0
// Iteration 1
// Iteration 2
// Function that returns a function
function multiplier(factor) {
return (number) => number * factor;
}
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
Array Methods with Functions
JavaScript arrays have powerful methods that use functions:
const numbers = [1, 2, 3, 4, 5];
// map - transform each element
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter - select elements that match condition
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]
// reduce - combine elements into single value
const sum = numbers.reduce((total, n) => total + n, 0);
console.log(sum); // 15
// find - get first matching element
const found = numbers.find(n => n > 3);
console.log(found); // 4
// some - check if any element matches
const hasEven = numbers.some(n => n % 2 === 0);
console.log(hasEven); // true
// every - check if all elements match
const allPositive = numbers.every(n => n > 0);
console.log(allPositive); // true
Practical Example: Task Manager
// Create a task manager using closures
function createTaskManager() {
let tasks = [];
let nextId = 1;
return {
addTask(description) {
const task = {
id: nextId++,
description,
completed: false
};
tasks.push(task);
return task;
},
completeTask(id) {
const task = tasks.find(t => t.id === id);
if (task) {
task.completed = true;
}
return task;
},
getTasks() {
return [...tasks]; // Return copy
},
getActiveTasks() {
return tasks.filter(t => !t.completed);
},
getCompletedTasks() {
return tasks.filter(t => t.completed);
}
};
}
// Usage
const manager = createTaskManager();
manager.addTask("Learn JavaScript");
manager.addTask("Build a project");
manager.completeTask(1);
console.log(manager.getActiveTasks());
// [{ id: 2, description: "Build a project", completed: false }]
Key Takeaways
- Functions are first-class citizens in JavaScript
- Arrow functions provide concise syntax and lexical
thisbinding - Scope determines variable accessibility (global, function, block)
- Closures allow functions to access outer scope variables
- Higher-order functions enable powerful abstractions
- Array methods like map, filter, and reduce use callback functions
- Closures are useful for creating private data and encapsulation
Practice Exercises
Exercise 1: Arrow Functions
Convert these traditional functions to arrow functions:
// Convert to arrow functions
function add(a, b) {
return a + b;
}
function isEven(num) {
return num % 2 === 0;
}
function greetUser(name) {
return `Welcome, ${name}!`;
}
Exercise 2: Array Methods
Use map, filter, and reduce to solve these problems:
const products = [
{ name: "Laptop", price: 999, category: "electronics" },
{ name: "Phone", price: 699, category: "electronics" },
{ name: "Shirt", price: 29, category: "clothing" },
{ name: "Shoes", price: 89, category: "clothing" }
];
// 1. Get all product names
// 2. Get all electronics products
// 3. Calculate total price of all products
// 4. Get products under $100
Exercise 3: Closure Challenge
Create a counter with increment, decrement, and reset methods:
// Create a function that returns an object with:
// - increment() - increases count by 1
// - decrement() - decreases count by 1
// - reset() - sets count back to 0
// - getValue() - returns current count
function createCounter() {
// Your code here
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
counter.getValue(); // 1
counter.reset(); // 0
Exercise 4: Higher-Order Functions
Create a function that returns customized greeting functions:
// Create a function that takes a greeting word
// and returns a function that greets with that word
function createGreeter(greeting) {
// Your code here
}
const sayHello = createGreeter("Hello");
const sayHi = createGreeter("Hi");
console.log(sayHello("Alice")); // "Hello, Alice!"
console.log(sayHi("Bob")); // "Hi, Bob!"
Common Pitfalls
1. Arrow Functions and 'this'
// Arrow functions don't have their own 'this'
const obj = {
name: "Object",
regularFunc: function() {
console.log(this.name); // "Object"
},
arrowFunc: () => {
console.log(this.name); // undefined (or global)
}
};
obj.regularFunc(); // Works as expected
obj.arrowFunc(); // Doesn't work as expected
2. Closure Memory
Additional Resources
What's Next?
In Lesson 03, we'll explore DOM manipulation and event handling. You'll learn how to make your web pages interactive by responding to user actions and dynamically updating content.