A function is a reusable, named block of code — write the steps once, then run them again and again with different inputs. They’re how you avoid repeating yourself, and how you break a big problem into smaller, readable pieces.
Arrow functions are the most common way to write a function in modern
JavaScript, especially for short, focused pieces of logic — the kind you
hand to .map, .filter, and event handlers:
const double = (n) => n * 2;
console.log(double(5)); // 10
const greet = (name) => {
const message = `Hello, ${name}!`;
return message;
};
console.log(greet("Ada")); // "Hello, Ada!"
Breaking down the syntax:
(n) — the parameter list, in parentheses. A single parameter can
skip the parentheses (n => n * 2), but keeping them is a common,
readable habit.=> — the “arrow” that separates the parameters from the body.n * 2 — for a single expression, the arrow function returns its
value automatically — no return keyword needed. This is called an
implicit return.{ ... } — when the body needs more than one statement, wrap it in
curly braces and use an explicit return to send back a value.The names inside the parentheses (name, n) are parameters — placeholders
for the values, called arguments, that get passed in when the function is
called. return sends a value back to wherever the function was called:
const add = (a, b) => a + b;
const total = add(2, 3); // total is 5
If a function never reaches a return, it implicitly returns undefined.
Two small features make functions more flexible about the arguments they accept — both common enough to know well:
// Default parameters — used when an argument is missing
const greet = (name = "friend") => `Hello, ${name}!`;
greet("Ada"); // "Hello, Ada!"
greet(); // "Hello, friend!"
// Rest parameters — gather any number of extra arguments into an array
const sum = (...numbers) => numbers.reduce((total, n) => total + n, 0);
sum(1, 2, 3, 4); // 10
A function declaration looks similar but uses the function keyword and a
name directly, without assigning it to a variable:
function double(n) {
return n * 2;
}
console.log(double(5)); // 10
The key difference from an arrow function assigned to a const is
hoisting: a function declaration is fully available even before the line
where it’s written, which makes it convenient for defining helpers near the
bottom of a file while using them near the top. Arrow functions don’t get
this treatment — they follow the normal “declare before use” rule that
const and let follow.
A function expression is a function created and stored in a variable using
the function keyword, rather than the arrow syntax:
const double = function (n) {
return n * 2;
};
You’ll see this form throughout older code and some libraries. It behaves
like an arrow function in most everyday situations — the main practical
difference is how each one treats this, which the Scope and Closures
lesson touches on.
Occasionally you’ll see a function defined and called in the very same breath — useful for running some setup code in its own private scope without leaving a named function lying around:
const result = (() => {
const setupValue = computeSomethingOnce();
return setupValue * 2;
})();
You’re unlikely to write many of these yourself, but recognizing the pattern — a function wrapped in parentheses, immediately followed by another set of parentheses to call it — helps when reading older code and certain libraries.
Before arrow functions arrived in ES2015, every function — short or long —
was written with the function keyword, either as a declaration or an
expression:
var numbers = [1, 2, 3];
var doubled = numbers.map(function (n) {
return n * 2;
});
Arrow functions trim this down considerably — the same line becomes
numbers.map((n) => n * 2) — which matters a lot when you’re passing small
functions around constantly, as you do with .map, .filter, and friends.
There’s a deeper reason arrow functions caught on, too: the function
keyword creates its own this, which famously caused confusion inside
methods and callbacks. Older code worked around it with a workaround
variable, often named self or that:
var counter = {
count: 0,
start: function () {
var self = this; // capture `this` before it changes inside the callback
setInterval(function () {
self.count += 1;
}, 1000);
},
};
Arrow functions don’t create their own this — they use the this from
where they’re written, which is almost always what you want. That single
difference, combined with their shorter syntax, is why arrow functions are
now the default choice for nearly every function you’ll write. The Scope and
Closures lesson — the final one in this series — explores this and related
ideas in more depth.
You may also encounter the old arguments object, an array-like value
available inside function-style functions that holds every argument
passed in, regardless of the declared parameters:
function sum() {
var total = 0;
for (var i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
Rest parameters (...numbers, shown earlier) replace this with a real
array that supports every array method you already know — another reason
modern functions read so much more simply than their older counterparts.