Skip to content
Topic 8 of 16
Theory

Arrow functions and closures

Why it matters

In JavaScript a function is a value, just like a number or a string. You can put it in a variable, pass it to another function, return it from a function. A lot is built on this: click handlers, the array methods map and filter (next topics), timers, server requests. And the short arrow syntax makes such code compact and readable.

Function expressions

You can create a function and put it in a variable:

const greet = function (name) {
  return `Hi, ${name}!`;
};
console.log(greet("Anna"));

Output:

Hi, Anna!

This is a function expression. Unlike the declaration function greet() {}, it can only be called after the line where it's created.

Arrow functions

The same function, shorter:

const greet = (name) => {
  return `Hi, ${name}!`;
};

And if the body is a single expression, the curly braces and return can be dropped: the result is returned automatically.

const greet = (name) => `Hi, ${name}!`;
const add = (a, b) => a + b;
const random = () => Math.random();   // no parameters — empty parentheses
const square = n => n * n;            // one parameter — parentheses can be omitted

console.log(add(2, 3), square(5));

Output:

5 25

Careful with objects: to return an object in the short form, wrap it in parentheses — () => ({ ok: true }). Otherwise the curly braces are taken as the function body.

Functions as arguments: callbacks

A function passed to another function so that it calls it is called a callback:

function repeat(times, action) {
  for (let i = 1; i <= times; i++) {
    action(i);
  }
}

repeat(3, (n) => console.log("Repeat #" + n));

Output:

Repeat #1
Repeat #2
Repeat #3

The repeat function doesn't know what to do — it only knows how many times. What to do is decided by the caller. That's how setTimeout(callback, ms), addEventListener("click", callback) and array methods work.

Functions that return functions

function multiplier(k) {
  return (n) => n * k;
}

const triple = multiplier(3);
const tenTimes = multiplier(10);
console.log(triple(5), tenTimes(5));

Output:

15 50

multiplier is a function "factory": every call creates a new function with its own k.

Closures

Notice: triple is called after multiplier has already finished. How does it remember k = 3? A function in JavaScript remembers the variables of the place where it was created. This is called a closure.

function makeGreeter(greeting) {
  let calls = 0;
  return (name) => {
    calls++;
    return `${greeting}, ${name}! (call ${calls})`;
  };
}

const hi = makeGreeter("Hello");
console.log(hi("Anna"));
console.log(hi("Timur"));

Output:

Hello, Anna! (call 1)
Hello, Timur! (call 2)

The variable calls lives as long as the function hi lives, and it's hidden: it can't be changed from outside. That's how JS makes "private" data.

How it works inside

Every function call creates an environment — the set of its variables. Normally, after leaving the function, the environment is thrown away. But if another function was created inside and returned outside, it keeps a reference to the environment, and the environment stays alive. Each new call of makeCounter() creates a new environment — that's why two counters don't interfere with each other.

Arrow functions have no this of their own and no prototype — so they're not used as constructors or as object methods that need this (more on this in the objects topic).

Common mistakes

  1. Curly braces without return. (n) => { n * 2 } returns undefined. Either remove the braces or add return.
  2. Calling instead of passing. repeat(3, sayHi()) passes the result of the call. You need repeat(3, sayHi).
  3. An object without parentheses. () => { a: 1 } is a function body, not an object. You need () => ({ a: 1 }).
  4. A shared variable outside the factory. If let count = 0 is written outside makeCounter, all counters will share one number.
  5. Calling an expression before it's created. const f = … can't be called above that line.

Cheat sheet

What How
Expression const f = function (x) { return x; };
Arrow with a body const f = (x) => { return x * 2; };
Short arrow const f = (x) => x * 2;
No parameters () => 42
Return an object () => ({ ok: true })
Callback repeat(3, (i) => console.log(i))
Closure a function remembers the variables of where it was created

An arrow

Easy+10 XP

Create a constant double — an arrow function that returns the number multiplied by 2.

double(4)   → 8
double(-5)  → -10

Connect the connector to solve tasks

Task code runs on your own computer — faster and more reliable than in the browser. This needs the free CodeWithMe connector app. You only connect it once.

Looking for the connector on this computer…

  1. 1
    Download the connector for your system (Windows, macOS or Linux).Download the connectorOn the download page pick the file with your system in its name, e.g. windows-amd64.
  2. 2Run it and choose a folder for your solutions. It will install Python and Node.js there by itself when needed.
  3. 3
    Enter below the 6-digit code shown in the connector window (“Code for CodeWithMe”). If the browser asks about local network access — allow it.

The connector is open source (on GitHub), works only on your computer and runs code only after you enter the pairing code.