If you’ve been following along with our guides on JavaScript operators and loops and iteration, you already know how to manipulate data and repeat tasks. The next natural step is learning how to package logic into reusable blocks — and that’s exactly what functions let you do.
Functions are the backbone of JavaScript. Every app, library, and framework you’ll ever touch is built out of them. In this guide, we’ll go from the absolute basics to some of the more advanced function concepts, with plenty of examples along the way.
What Is a Function?
A function is a reusable block of code designed to perform a specific task. Instead of writing the same logic again and again, you define it once and “call” it whenever you need it.
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Ashutosh")); // Hello, Ashutosh!
Think of a function like a machine: you feed it input (called parameters), it does some work, and it gives you an output (the return value).
Function Declarations
The most common way to create a function is the function declaration syntax:
function add(a, b) {
return a + b;
}
console.log(add(5, 10)); // 15
A key feature of function declarations is hoisting — they’re moved to the top of their scope during compilation, so you can call them before they appear in your code:
sayHi(); // Works fine!
function sayHi() {
console.log("Hi there!");
}
Function Expressions
A function expression assigns a function to a variable. Unlike declarations, these are not hoisted the same way — you can’t call them before they’re defined.
const multiply = function (a, b) {
return a * b;
};
console.log(multiply(4, 5)); // 20
Function expressions are especially useful when you want to pass a function around as a value — for example, as a callback.
Arrow Functions
Introduced in ES6, arrow functions offer a shorter syntax and behave differently with the this keyword (they don’t have their own this — they inherit it from the surrounding scope).
const square = (num) => num * num;
console.log(square(6)); // 36
// Multiple parameters
const add = (a, b) => a + b;
// No parameters
const sayHello = () => console.log("Hello!");
// Multiple statements need curly braces and an explicit return
const divide = (a, b) => {
if (b === 0) return "Cannot divide by zero";
return a / b;
};
Arrow functions are widely used with array methods like map(), filter(), and reduce() — the same kind of iteration you saw in our loops guide, just expressed in a more functional style.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
Default Parameters
You can assign default values to parameters, which kick in if no argument (or undefined) is passed.
function greet(name = "Guest") {
return `Welcome, ${name}!`;
}
console.log(greet()); // Welcome, Guest!
console.log(greet("Rahul")); // Welcome, Rahul!
Rest Parameters
Rest parameters let a function accept an indefinite number of arguments as an array — handy when you don’t know how many values will be passed in.
function sumAll(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sumAll(1, 2, 3)); // 6
console.log(sumAll(5, 10, 15, 20)); // 50
Notice the reduce() call above — this combines a function with an iteration pattern, similar to the for and while loops covered in our loops and iteration guide.
Return Values
A function doesn’t have to return anything. If there’s no return statement, it implicitly returns undefined.
function logMessage(msg) {
console.log(msg);
// no return statement
}
const result = logMessage("Testing");
console.log(result); // undefined
You can also return early to exit a function based on a condition — a pattern that pairs naturally with the comparison and logical operators from our operators guide:
function checkAge(age) {
if (age < 18) {
return "Not eligible";
}
return "Eligible";
}
Function Scope and Closures
Variables declared inside a function are only accessible within that function — this is called function scope.
function outer() {
let message = "Hi";
console.log(message); // Hi
}
console.log(message); // ReferenceError: message is not defined
A closure happens when an inner function “remembers” variables from its outer function, even after the outer function has finished running.
function counter() {
let count = 0;
return function () {
count++;
return count;
};
}
const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2
console.log(increment()); // 3
Closures are one of the most powerful (and most asked-about in interviews) features of JavaScript. They’re the foundation behind things like data privacy and function factories.
Higher-Order Functions
A higher-order function either takes another function as an argument, returns a function, or both.
function greetUser(name, formatter) {
return formatter(name);
}
const upperCaseFormatter = (name) => name.toUpperCase();
console.log(greetUser("ashutosh", upperCaseFormatter)); // ASHUTOSH
Array methods like map(), filter(), and forEach() are all higher-order functions built into JavaScript.
Immediately Invoked Function Expressions (IIFE)
An IIFE runs as soon as it’s defined — no separate call needed. It’s often used to create an isolated scope, avoiding polluting the global namespace.
(function () {
console.log("This runs immediately!");
})();
Callback Functions
A callback is simply a function passed into another function to be executed later — often after some operation completes.
function processData(data, callback) {
console.log("Processing data...");
callback(data);
}
processData("User Info", (data) => {
console.log(`Done processing: ${data}`);
});
Callbacks are the foundation of asynchronous JavaScript — things like setTimeout(), event listeners, and (eventually) Promises all build on this idea.
Best Practices for Writing Functions
- Keep functions small and focused — one function should do one thing well.
- Name functions clearly —
calculateTotal()is better thandoStuff(). - Avoid too many parameters — if you need more than 3–4, consider passing an object instead.
- Use arrow functions for short, simple operations, and regular functions when you need your own
thisbinding. - Return early to avoid deeply nested
ifblocks.
Wrapping Up
Functions tie together everything you’ve learned about operators and loops into reusable, testable pieces of logic. Once you’re comfortable with declarations, expressions, arrow functions, and closures, you’ll be ready to explore more advanced topics like Promises, async/await, and functional programming patterns.
If you haven’t already, check out our guides on JavaScript operators and loops and iteration to round out your fundamentals.