Functions and the call stack

Introduction

A function is a named piece of code you can run (call) many times with different values. Functions are how programs stay manageable: instead of repeating the same ten lines, you give them a name, and when something is wrong you fix it in one place. A good function name also explains what the code means, so that the main program reads almost like a list of steps.

How it works

function area(width, height) {
  return width * height;
}

width and height are parameters, and the values passed in a call, area(3, 4), are arguments. return sends a value back to the caller and ends the function. A function without return gives back undefined.

The call stack. Each call gets its own frame: a box holding its parameters and variables. Frames are kept on the call stack: a call puts a new frame on top, and a return takes it off and continues the line that called it. This is why two calls cannot disturb each other's variables.

More ways to write a function.

FormExample
declarationfunction double(x) { return x * 2; }
function expressionconst double = function (x) { return x * 2; };
arrow functionconst double = (x) => x * 2;
default parameterfunction greet(name = "friend") { ... }

Arrow functions with one expression return it without writing return. Declarations are hoisted: they can be called before the line where they are written.

Recursion. A function may call itself, as long as each call gets closer to a base case that stops. It suits problems that contain smaller copies of themselves, such as folders inside folders:

// Recursion: n! = n x (n - 1)!, and 1! = 1.
function factorial(n) {
  if (n <= 1) {
    return 1;               // the base case
  }
  return n * factorial(n - 1);
}

const result = factorial(4);
console.log("4! =", result);
Open in compiler

The stack grows by one frame per call, then shrinks as each call returns its answer to the one below.

Syntax

function name(parameter1, parameter2 = "default") {
  return parameter1 + parameter2;
}
const shorter = (a, b) => a + b;

Example

Step through it and watch the frames: room calls area, so for a moment there are three frames (the global code, room, area), each with its own variables.

// Functions calling functions: watch the call stack.
function area(width, height) {
  const result = width * height;
  return result;
}

function room(name, w, h) {
  const size = area(w, h);
  return `${name}: ${size} square metres`;
}

console.log(room("Kitchen", 3, 4));
console.log(room("Bedroom", 4, 5));
Open in compiler

Line by line

  1. The two function statements only define the functions. Nothing runs yet.
  2. console.log(room("Kitchen", 3, 4)) must call room first, to find out what to print.
  3. In room, area(w, h) starts a new frame, in which width is 3 and height is 4.
  4. area returns 12 and its frame is removed. size receives the 12.
  5. room returns a sentence, which is printed.
  6. The second call does the same with new values, and new frames.

Expected result

Kitchen: 12 square metres
Bedroom: 20 square metres

In the real world

Every button on a website runs a function. Libraries are collections of functions other people wrote and tested. Recursion walks tree-shaped data, such as the comments under a post, the folders on a disk and the elements of a web page.

Common mistakes

  • Forgetting return. The function gives back undefined.
  • Printing instead of returning. console.log shows a value, but the caller cannot use it.
  • A recursive function with no base case. The stack overflows, which is a RangeError.
  • Calling a function without the brackets. area is the function itself, and area(3, 4) calls it.

Best practices

  • Give each function one job, and a name that describes the job.
  • Return values rather than printing inside the function, so that it can be reused.
  • Keep the number of parameters small.
  • Prefer a loop when recursion gains nothing, because every call uses stack space.

Summary

Functions take parameters, do work and return a value. Each call has its own frame on the call stack. Arrow functions are a short form, and default parameters fill in missing arguments. Recursion solves a problem through smaller versions of itself, and must have a base case.

Quick check

1. What does a function without a return statement give back?
2. In area(3, 4), what are 3 and 4?
3. What happens to a function's frame when it returns?
4. What does every recursive function need?

Key takeaways

  • A function without return gives back undefined.
  • Each call gets its own frame with its own variables.
  • Every recursive function needs a base case that stops it.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.