Functions in JavaScript
What is a Function?
A function in JavaScript is a reusable block of code designed to perform a specific task. Instead of writing the same logic multiple times, you define it once and call it whenever needed. Functions improve readability, reduce repetition, and make programs easier to maintain and test.
The basic syntax for defining and calling a function looks like this:
// Defining a function
function functionName(parameters) {
// statements
}
// Calling a function
functionName();
// Simple example
function sayHello() {
console.log('Hello World!');
}
sayHello(); // Hello World!
1. Function Declaration
A function declaration defines a named function using the function keyword. Function declarations are hoisted — the JavaScript engine moves them to the top of their scope, so you can call them before they appear in the code.
// Can be called before it is defined (hoisting)
console.log(greet('Alice')); // Hello, Alice!
function greet(name) {
return `Hello, ${name}!`;
}
// Default parameter
function add(a, b = 0) {
return a + b;
}
console.log(add(5)); // 5
console.log(add(5, 3)); // 8
// Rest parameters — collects remaining args into an array
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
2. Function Expression
A function expression assigns a function to a variable. Unlike declarations, function expressions are not hoisted — you must define them before calling them. They can be anonymous (no name) or named.
Named function expressions are useful for recursion and produce clearer stack traces during debugging.
// Anonymous function expression
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(4, 5)); // 20
// Named function expression — name is only visible inside the function
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
console.log(factorial(5)); // 120
// Must be defined before calling (not hoisted)
// console.log(multiply(2, 3)); // ReferenceError if called before definition
3. Arrow Functions (ES6+)
Arrow functions provide a shorter syntax for writing functions. They are always anonymous and have two important differences from regular functions:
// Single parameter — no parentheses needed
const square = n => n * n;
console.log(square(5)); // 25
// Multiple parameters — parentheses required
const add = (a, b) => a + b;
console.log(add(3, 4)); // 7
// Multi-line body — curly braces and explicit return required
const divide = (a, b) => {
if (b === 0) throw new Error('Division by zero');
return a / b;
};
console.log(divide(10, 2)); // 5
// Arrow functions shine in array methods
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // [2,4,6,8,10]
const evens = numbers.filter(n => n % 2 === 0); // [2,4]
const total = numbers.reduce((sum, n) => sum + n, 0); // 15
4. Higher-Order Functions
A higher-order function is a function that either takes another function as an argument, returns a function, or both. This is a core concept in functional programming and is the foundation of built-in methods like map(), filter(), and reduce().
// Takes a function as an argument
function applyTwice(fn, value) {
return fn(fn(value));
}
const double = x => x * 2;
console.log(applyTwice(double, 3)); // 12
// Returns a function (function factory using closure)
function multiplier(factor) {
return n => n * factor;
}
const triple = multiplier(3);
const times5 = multiplier(5);
console.log(triple(4)); // 12
console.log(times5(4)); // 20
// Callback pattern
function fetchData(url, onSuccess, onError) {
setTimeout(() => {
if (url) onSuccess({ data: 'result' });
else onError(new Error('No URL provided'));
}, 1000);
}
fetchData('/api/data', data => console.log(data), err => console.error(err));
5. IIFE — Immediately Invoked Function Expression
An IIFE is a function that is defined and called at the same time. It creates a private scope, preventing variables from leaking into the global scope. IIFEs were widely used before ES6 modules became standard.
// Classic IIFE — runs immediately, creates private scope
(function() {
const privateVar = 'I am private';
console.log(privateVar); // I am private
})();
// console.log(privateVar); // ReferenceError — not accessible outside
// Arrow function IIFE
const result = (() => {
const x = 10, y = 20;
return x + y;
})();
console.log(result); // 30
Function Declaration vs Expression vs Arrow
Here is a quick comparison of the three main function types to help you choose the right one:
// Declaration — hoisted, has own this, can be constructor
function Declaration(x) { return x; }
// Expression — not hoisted, has own this, can be constructor
const Expression = function(x) { return x; };
// Arrow — not hoisted, NO own this, NOT a constructor
const Arrow = x => x;
// When to use each:
// Declaration → top-level utility functions, methods that need hoisting
// Expression → callbacks stored in variables, conditional function assignment
// Arrow → short callbacks, array methods, when you need lexical this
- Function declarations are hoisted — they can be called before they appear in the code. Function expressions are NOT hoisted.
- Arrow functions do not have their own this — they inherit it from the enclosing scope. Use regular functions when you need a dynamic this.
- Default parameters (b = 0) handle missing arguments gracefully without needing manual checks.
- Rest parameters (...args) collect all remaining arguments into an array — useful for variadic functions.
- Higher-order functions take or return other functions — the foundation of map(), filter(), and reduce().
- IIFEs create a private scope immediately — useful for isolating code and avoiding global variable pollution.