JavaScript Functions
Functions are one of the most fundamental concepts in JavaScript. They allow you to group code together, reuse it, and make your programs more organized and maintainable.
What are Functions?
Functions are reusable blocks of code that perform specific tasks. They can take input (parameters), process the input, and return output (return values).
Declaring Functions
1. Function Declaration
The most common way to declare a function:
function greet(name) {
return "Hello, " + name + "!";
}
// Calling the function
console.log(greet("Alice")); // Outputs: Hello, Alice!
2. Function Expression
Assign a function to a variable:
const greet = function(name) {
return "Hello, " + name + "!";
};
console.log(greet("Bob")); // Outputs: Hello, Bob!
3. Arrow Functions (ES6)
Modern syntax for writing functions:
// Simple arrow function
const greet = (name) => {
return "Hello, " + name + "!";
};
// Arrow function with implicit return
const greet = (name) => "Hello, " + name + "!";
// Arrow function with single parameter (parentheses optional)
const greet = name => "Hello, " + name + "!";
console.log(greet("Charlie")); // Outputs: Hello, Charlie!
Function Parameters
1. Basic Parameters
function add(a, b) {
return a + b;
}
console.log(add(5, 3)); // Outputs: 8
2. Default Parameters
ES6 allows you to set default values for parameters:
function greet(name = "Guest") {
return "Hello, " + name + "!";
}
console.log(greet()); // Outputs: Hello, Guest!
console.log(greet("Alice")); // Outputs: Hello, Alice!
3. Rest Parameters
Handle multiple parameters as an array:
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // Outputs: 15
console.log(sum(10, 20)); // Outputs: 30
Return Values
1. Single Return Value
function multiply(a, b) {
return a * b;
}
const result = multiply(4, 5);
console.log(result); // Outputs: 20
2. Multiple Return Values
Return an object or array to return multiple values:
function getUserInfo(id) {
// Simulate database lookup
if (id === 1) {
return {
name: "John Doe",
age: 30,
email: "[email protected]"
};
}
return null;
}
const user = getUserInfo(1);
console.log(user.name); // Outputs: John Doe
console.log(user.age); // Outputs: 30
3. Early Returns
Exit a function early based on conditions:
function getDiscount(price, memberLevel) {
if (!memberLevel) {
return price; // No discount for non-members
}
if (memberLevel === "gold") {
return price * 0.8; // 20% discount
}
if (memberLevel === "silver") {
return price * 0.9; // 10% discount
}
return price; // No discount for other levels
}
console.log(getDiscount(100, "gold")); // Outputs: 80
console.log(getDiscount(100, "silver")); // Outputs: 90
console.log(getDiscount(100, null)); // Outputs: 100
Function Scope
1. Global and Local Scope
const globalVar = "I'm global";
function testScope() {
const localVar = "I'm local";
console.log(globalVar); // Can access global
console.log(localVar); // Can access local
}
testScope();
console.log(globalVar); // Outputs: I'm global
// console.log(localVar); // Error: localVar is not defined
2. Block Scope (let and const)
function testBlockScope() {
if (true) {
const blockVar = "I'm block-scoped";
console.log(blockVar); // Outputs: I'm block-scoped
}
// console.log(blockVar); // Error: blockVar is not defined
}3. Closures
Functions can remember and access variables from their outer scope:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // Outputs: 1
console.log(counter()); // Outputs: 2
console.log(counter()); // Outputs: 3
Higher-Order Functions
Functions that take other functions as arguments or return functions:
1. Functions as Arguments
function calculate(operation, a, b) {
return operation(a, b);
}
function add(x, y) {
return x + y;
}
function multiply(x, y) {
return x * y;
}
console.log(calculate(add, 5, 3)); // Outputs: 8
console.log(calculate(multiply, 5, 3)); // Outputs: 15
2. Functions that Return Functions
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // Outputs: 10
console.log(triple(5)); // Outputs: 15
Built-in Higher-Order Functions
1. Array Methods
const numbers = [1, 2, 3, 4, 5];
// map: Transform each element
const doubled = numbers.map(num => num * 2);
console.log(doubled); // Outputs: [2, 4, 6, 8, 10]
// filter: Keep elements that pass a test
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // Outputs: [2, 4]
// reduce: Reduce array to single value
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // Outputs: 15
// forEach: Execute function for each element
numbers.forEach(num => console.log(num * 3));
// Outputs: 3, 6, 9, 12, 15
2. String Methods
const text = "Hello World";
// toUpperCase: Convert to uppercase
console.log(text.toUpperCase()); // Outputs: HELLO WORLD
// replace: Replace text
console.log(text.replace("World", "JavaScript")); // Outputs: Hello JavaScript
// split: Split string into array
console.log(text.split(" ")); // Outputs: ["Hello", "World"]
Function Methods
1. call() and apply()
Execute a function with a specific this value:
const person = {
name: "John",
age: 30
};
function greet(greeting) {
return greeting + ", I'm " + this.name + " and I'm " + this.age + " years old.";
}
console.log(greet.call(person, "Hi")); // Outputs: Hi, I'm John and I'm 30 years old.
console.log(greet.apply(person, ["Hello"])); // Outputs: Hello, I'm John and I'm 30 years old.
2. bind()
Create a new function with a specific this value:
const person = {
name: "Alice"
};
function greet() {
return "Hello, I'm " + this.name;
}
const boundGreet = greet.bind(person);
console.log(boundGreet()); // Outputs: Hello, I'm Alice
Recursive Functions
Functions that call themselves:
// Factorial using recursion
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(5)); // Outputs: 120
// Fibonacci sequence
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(7)); // Outputs: 13
Immediately Invoked Function Expressions (IIFE)
Functions that execute immediately after being defined:
(function() {
const message = "Hello from IIFE!";
console.log(message);
})(); // Outputs: Hello from IIFE!
// With parameters
(function(name) {
console.log("Hello, " + name + "!");
})("World"); // Outputs: Hello, World!
Pure Functions
Functions that always return the same output for the same input and have no side effects:
// Pure function
function add(a, b) {
return a + b;
}
// Impure function (has side effects)
let total = 0;
function addToTotal(value) {
total += value; // Modifies external variable
return total;
}
// Better pure function
function addValues(a, b) {
return a + b;
}Function Best Practices
1. Use Descriptive Names
// Good
function calculateArea(length, width) {
return length * width;
}
// Bad
function calc(l, w) {
return l * w;
}2. Keep Functions Small
// Good - Single responsibility
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function sanitizeInput(input) {
return input.trim().toLowerCase();
}
// Bad - Multiple responsibilities
function processEmail(email) {
const trimmed = email.trim().toLowerCase();
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(trimmed);
}3. Use Default Parameters
// Good
function createUser(name, role = "user") {
return { name, role };
}
// Avoid this pattern
function createUser(name, role) {
if (!role) {
role = "user";
}
return { name, role };
}4. Document Your Functions
/**
* Calculates the area of a rectangle
* @param {number} length - The length of the rectangle
* @param {number} width - The width of the rectangle
* @returns {number} The area of the rectangle
* @throws {Error} If length or width is negative
*/
function calculateArea(length, width) {
if (length < 0 || width < 0) {
throw new Error("Length and width must be positive");
}
return length * width;
}Error Handling in Functions
function divide(a, b) {
try {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
} catch (error) {
console.error("Error:", error.message);
return null;
}
}
console.log(divide(10, 2)); // Outputs: 5
console.log(divide(10, 0)); // Outputs: Error: Cannot divide by zero, then null
Complete Example
// A complete example showing various function concepts
// Data validation function
function validateUser(userData) {
const errors = [];
if (!userData.name || userData.name.trim().length < 2) {
errors.push("Name must be at least 2 characters");
}
if (!userData.email || !validateEmail(userData.email)) {
errors.push("Valid email is required");
}
if (userData.age && (userData.age < 0 || userData.age > 150)) {
errors.push("Age must be between 0 and 150");
}
return {
isValid: errors.length === 0,
errors: errors
};
}
// Email validation helper function
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// User creation function with error handling
function createUser(userData) {
const validation = validateUser(userData);
if (!validation.isValid) {
throw new Error("Validation failed: " + validation.errors.join(", "));
}
return {
id: generateId(),
name: userData.name.trim(),
email: userData.email.toLowerCase(),
age: userData.age || null,
createdAt: new Date().toISOString()
};
}
// ID generator function (closure)
function createIdGenerator() {
let lastId = 0;
return function() {
lastId++;
return "user_" + lastId;
};
}
const generateId = createIdGenerator();
// User processing function (higher-order function)
function processUsers(users, processor) {
return users.map(processor);
}
// Usage examples
try {
const userData = {
name: "John Doe",
email: "[email protected]",
age: 30
};
const user = createUser(userData);
console.log("Created user:", user);
// Process multiple users
const users = [
{ name: "Alice", email: "[email protected]" },
{ name: "Bob", email: "[email protected]" }
];
const processedUsers = processUsers(users, userData =>
createUser({ ...userData, age: 25 })
);
console.log("Processed users:", processedUsers);
} catch (error) {
console.error("Error:", error.message);
}External Resources:
Related Tutorials: