Dataslope logoDataslope

Functions — the Building Blocks

The single most important concept in programming — giving a name to a piece of work, and reusing it.

If there is one concept in this whole course you should take the time to truly understand, it is functions. Everything else — data structures, classes, asynchronous programming, frameworks — is built on top of functions. They are the atom of programming.

What a function is

A function is a named, reusable piece of code that takes some inputs (arguments) and produces an output (a return value). You define it once. You call it many times.

That picture is the entire mental model. Inputs in, work happens, output out.

Declaring a function

The classic syntax:

function name(parameter1, parameter2) {
  // body
  return someValue;
}

A real example:

Code Block
JavaScript ES2023+

Notice four things:

  • The declaration is a recipe — it does not execute the body yet.
  • A call like double(3) executes the body, with n bound to 3.
  • The function does its work and returns a value.
  • The caller can then use that value.

If a function has no return (or return with nothing after it), the result is undefined.

Why functions matter

It's tempting to write your whole program as one long script. For twenty lines, that's fine. For two hundred, it becomes a maze. The moment you find yourself copying-and-pasting a chunk of code with small tweaks, you should be writing a function instead.

Functions give you three huge benefits:

  1. Reuse. Write it once, use it everywhere.
  2. Naming. A well-named function is documentation. tip() is easier to read than the formula inside it.
  3. Isolation. What happens inside a function stays mostly inside it. You can think about the function on its own without reading the whole program.

Compare these two equivalent programs:

Code Block
JavaScript ES2023+
Code Block
JavaScript ES2023+

Both programs produce the same output. One is a maintenance nightmare; the other is a pleasure. The difference is just one function.

Function expressions and arrow functions

JavaScript has more than one syntax for defining functions. They all produce the same kind of value.

Function declaration

function add(a, b) {
  return a + b;
}

Function expression

A function can also appear as a value on the right-hand side of an assignment:

const add = function (a, b) {
  return a + b;
};

Arrow function

The most modern, compact syntax. Especially common for short callbacks:

const add = (a, b) => a + b;

Arrow functions have a few useful shorthand rules:

  • If there is exactly one parameter, the parentheses are optional: n => n * 2.
  • If the body is a single expression, the {} and return are optional: n => n * 2.
  • For a multi-line body, use {} and an explicit return as normal: (n) => { console.log(n); return n * 2; }.
Code Block
JavaScript ES2023+

(There are a couple of subtle differences between regular functions and arrow functions — chiefly around the this keyword. We will look at this later. For now, treat them as near-interchangeable.)

Functions are first-class values

This is the deep idea borrowed from Scheme back in 1995, and it is the secret weapon of modern JavaScript.

In JavaScript, functions are values. You can put them in variables, pass them to other functions, return them from functions, and store them in arrays and objects — exactly like numbers and strings.

Code Block
JavaScript ES2023+

If this feels like magic, that's normal — and important. We will return to it in the closures chapter.

Pure functions and side effects

A pure function has two properties:

  1. Given the same inputs, it always returns the same output.
  2. It does not affect anything outside itself — no printing, no variable changes, no network calls, no file writes.

A function with side effects does some kind of work the outside world can observe: printing, writing files, modifying a shared variable.

Pure functions are easier to think about, easier to test, and easier to reuse. Side effects are sometimes necessary — eventually you do want output and persistence — but the goal of a good design is to keep them in well-marked, predictable places.

Code Block
JavaScript ES2023+

Whenever you can, write pure functions and put the side effects on the edges (a single function that calls console.log, a single place that touches the database). This single discipline will, more than almost anything else, make your code maintainable.

Reading the call stack

Every time you call a function, the runtime pushes a new entry onto its call stack. When the function returns, the entry is popped off. This is how the runtime always knows "where to return to".

In a simple program:

function bigName(name) {
  return name.toUpperCase();
}

function greet(name) {
  const big = bigName(name);   // push bigName onto the stack
  return "Hello, " + big;      // bigName returns; pop it off
}

console.log(greet("Ada"));     // push greet onto the stack
                               // (greet calls bigName inside)

At the moment bigName is running, the call stack looks like:

[ bigName ]   <- currently running
[ greet   ]
[ console.log ] (well, the runtime's outer frame)

When bigName returns, it is removed, and we continue running greet.

You will see the call stack again when we discuss debugging — most error messages include a stack trace that lists every function that was in progress when the error happened. Learning to read that trace is a superpower.

A multi-file functions example

Real-world programs split functions across files. Each file exports the functions it owns; other files import them. Here is a small two-file example that does exactly that.

Code Block
JavaScript ES2023+

bill.js is a small module — a collection of related functions, exported for use by other files. index.js is the driver — it imports those functions and uses them. We will see this pattern many more times.

Challenge

Challenge
JavaScript ES2023+
Reusable formula

Define a function bmi(weightKg, heightM) that returns the Body Mass Index, using the formula:

bmi = weight / (height * height)

Then define a function bmiCategory(bmi) that returns:

  • "underweight" if bmi < 18.5
  • "normal" if 18.5 ≤ bmi < 25
  • "overweight" if 25 ≤ bmi < 30
  • "obese" if bmi ≥ 30

QuestionSelect one

Which of these statements about JavaScript functions is true and most distinctive of the language?

Functions can only be called once

Functions cannot be assigned to variables

Functions are first-class values: they can be stored in variables, passed as arguments, and returned from other functions

Functions must always be declared before they're called

On this page