Viscosity
Review
Part I · JavaScript for engine builders · Day 2

5. Functions as values

A function is a value, just like 3 or a string. You can store it, hand it to another function, and get one back. That one fact turns a pile of nearly identical loops into one.

About 18 minutes, including the exercises and quiz
By the end you will be able to
  • Store a function in a variable and pass it to another function as a callback.
  • Write arrow functions, and say when they need braces and a return.
  • Transform, select and combine arrays with map, filter and reduce.
  • Write a while loop, and stop or skip with break and continue.

Two loops, one shape

Here are some particles, and two jobs: collect every x, and collect the particles that move right.

const ps = [
  { x: 1, vx: 2, mass: 5 },
  { x: 4, vx: -1, mass: 2 },
  { x: 9, vx: 3, mass: 1 },
];

const xs = [];
for (let i = 0; i < ps.length; i++) {
  xs.push(ps[i].x);
}

const right = [];
for (let i = 0; i < ps.length; i++) {
  if (ps[i].vx > 0) {
    right.push(ps[i]);
  }
}

Apart from one line each, these are the same loop. Chapter 1 said: when work repeats, name it. But here the part that differs is itself a piece of work: “take the x”, “is it moving right?”. To write the loop once, you need to hand a piece of work to a function.

Intuition

So far a function has been a recipe card on a shelf. But a card is also a thing you can hand to a friend: “do this to every item in the box”. The friend runs the loop. You only supply the recipe for one item.

A function is a value

function double(n) {
  return n * 2;
}

const twice = double;
const ops = [double];

console.log(twice(5));         // 10
console.log(ops[0](7));        // 14
console.log(twice === double); // true

const twice = double has no parentheses, so nothing runs: it copies the function into a new name. In JavaScript every function is an object[3], so chapter 4’s rules apply: the variable holds an arrow, and copying it makes an alias.

The names double and twice, and slot 0 of the array ops, all hold arrows to one function object in memory.NAMESIN MEMORYdoubletwiceops[0]A FUNCTION OBJECT(n) {return n * 2;}
Function names hold arrows, like any object. double, twice and the array slot ops[0] all point at one function object. Calling any of them runs the same code.
Definition

JavaScript has first-class functions: a function is a value like any other. It can be stored in a variable, an array or an object, passed as an argument, and returned from another function[1]. double is the function. double(5) calls it and is the result, 10.

Handing work over: callbacks

Now the loop can be written once. The part that differs comes in as a parameter, get:

function pluck(ps, get) {
  const out = [];
  for (let i = 0; i < ps.length; i++) {
    out.push(get(ps[i]));
  }
  return out;
}

function getX(p) {
  return p.x;
}

console.log(pluck(ps, getX)); // [ 1, 4, 9 ]

We pass getX, not getX(). pluck decides when to call it, and with which particle. Passing getX(ps[0]) would hand over the result, the number 1, and pluck would fail trying to call a number.

A function can also return a function. The function (n) { ... } here has no name: it’s a function written where a value goes.

function times(k) {
  return function (n) {
    return n * k;
  };
}

const triple = times(3);
console.log(triple(5)); // 15
Definition

A callback is a function passed to another function, which calls it when it needs to[2]. A function that takes a function as an argument, or returns one, is a higher-order function. pluck and times are both higher-order.

Something odd is going on in times. Chapter 1 said a call’s page is thrown away when it returns. So how does triple still know that k is 3? Hold that thought: it is the next chapter.

Arrow functions

Small callbacks are so common that JavaScript has a short way to write them[4]:

const double = (n) => n * 2;
const add = (a, b) => a + b;
const getX = (p) => p.x;

Read => as “becomes”: “n becomes n * 2”.

Definition

An arrow function is written (params) => expression or (params) => { statements }. The first form returns the expression. The second, for longer bodies, needs a return like any other function.

Pitfall

(n) => { n * 2 } returns undefined. The braces turn n * 2 into a statement, and nothing returns it. Drop the braces or add return.

map, filter and reduce

Arrays come with higher-order methods that run the common loops for you.

Intuition

Think of a factory line. map is a station that changes every item that passes. filter is an inspector who lets only some items through. reduce is the box at the end that combines everything into one thing.

Step through all three. Watch what happens to each value your callback returns:

Step throughfilter, map and reduce, one call at a time
const xs = [1, 4, 9];
const big = xs.filter((n) => n > 3);
const tens = big.map((n) => n * 10);
const sum = tens.reduce(
(t, n) => t + n, 0);
The input array on top, the callback call in the middle, and the output underneathINPUT: xs149
An ordinary array of three numbers. Watch it: none of the three methods below will change it.
1/9
Definition

arr.map(f) returns a new array of the same length, holding f(item) for every item. arr.filter(f) returns a new array of the items for which f returned true. arr.reduce(f, start) keeps a running value that begins as start. For each item it sets that value to f(value, item), and at the end it returns the value. None of them change the original array.

So the two loops from the start of the chapter shrink to one line each:

const xs = ps.map((p) => p.x);
const right = ps.filter((p) => p.vx > 0);

Always give reduce its starting value. Without one, it uses the first item instead, and on an empty array there is no first item, so it throws a TypeError[5].

Try itmap, filter and reduce on particles
const ps = [
  { x: 1, vx: 2, mass: 5 },
  { x: 4, vx: -1, mass: 2 },
  { x: 9, vx: 3, mass: 1 },
];

const xs = ps.map((p) => p.x);
console.log(xs);

const right = ps.filter(
  (p) => p.vx > 0,
);
console.log(right.length);

const mass = ps.reduce(
  (t, p) => t + p.mass,
  0,
);
console.log(mass);
console.log(ps.length);

Loops that don’t count: while

A for loop is built for counting. A while loop just repeats while a condition holds, and you move things along yourself. break leaves a loop at once. continue skips the rest of this pass and goes back to the check. Both work in for loops too.

const vs = [3, -1, 8, 12, 5];
let i = 0;
let total = 0;
while (i < vs.length) {
  const v = vs[i];
  i++;
  if (v < 0) {
    continue; // skip negatives
  }
  if (v > 10) {
    break; // stop at the first big one
  }
  total = total + v;
}
console.log(total); // 11
Definition

A while loop, while (cond) { ... }, checks cond before every pass and runs the body while it is true[6]. If cond starts out false, the body never runs. break jumps out of the loop. continue jumps to the next check.

Notice that i++ comes before the continue. If it came after, a negative number would skip the i++, i would never move again, and the loop would run forever.

Under the hood

In the interpreter you’ll write in Part III, a function value is literally a small object holding the parameter names and the body. Calling it means running that body on a fresh page. To the interpreter, a function is just one more kind of value, which is why it can be passed around.

Look again at ps.map((p) => p.x). It reads one field from every record. That is the access pattern a struct of arrays makes cheap, because every x is already side by side. To choose a layout, Viscosity has to see inside callbacks like this one and work out which fields a loop touches.

Your turn

ExerciseCount the items that pass a test

Write countIf(xs, test). test is a callback that takes one item and returns true or false. Return how many items of xs make test return true. For example countIf([1, 5, 8], (n) => n > 4) is 2.

function countIf(xs, test) {
  // your code here
}

const big = (n) => n > 4;
console.log(countIf([1, 5, 8], big));
Extra practiceAdd up one field with reduce

Write sumBy(ps, f) using reduce. It returns the total of f(p) over every item p in ps. For example sumBy(ps, (p) => p.mass) is the total mass. An empty array must give 0.

function sumBy(ps, f) {
  // your code here
}

const ps = [{ mass: 5 }, { mass: 2 }];
console.log(sumBy(ps, (p) => p.mass));
Extra practiceHow many doublings?

Write doublings(n) with a while loop. Start from 1 and keep doubling. Return how many doublings it takes to reach at least n. So doublings(1) is 0, doublings(2) is 1, and doublings(5) is 3 (1 → 2 → 4 → 8).

function doublings(n) {
  // your code here
}

console.log(doublings(5));

What you learned

  • Functions are first-class: values you can store, pass and return. f is the function; f() calls it.
  • A callback is a function you hand to another function to call. A higher-order function takes or returns functions.
  • Arrow functions: (x) => x * 2 returns its expression. With braces, you need return.
  • map transforms every item, filter keeps some, reduce combines them into one. All three build new values and leave the array alone.
  • A while loop runs while its condition holds. break leaves, continue skips ahead.

Next: how triple remembered its k.

Check your understanding

0/6
  1. 1.

    What does this print?

    const half = (n) => n / 2;
    const h = half;
    console.log(h(10));
  2. 2.

    run(f) calls f three times. Which line hands it a callback?

  3. 3.

    What is [1, 2, 3].map((n) => { n * 2 })?

  4. 4.

    What is [2, 3, 4].reduce((t, n) => t + n, 10)?

  5. 5.

    How many times does the body run?

    let i = 10;
    while (i < 3) {
      i++;
    }
  6. 6.

    Which of these is a higher-order function?

References

  1. [1] MDN contributors. First-class function (Glossary). MDN Web Docs. developer.mozilla.org/en-US/docs/Glossary/First-class_Function (checked 2026-10-31)
  2. [2] MDN contributors. Callback function (Glossary). MDN Web Docs. developer.mozilla.org/en-US/docs/Glossary/Callback_function (checked 2026-10-31)
  3. [3] MDN contributors. Function. MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function (checked 2026-10-31)
  4. [4] MDN contributors. Arrow function expressions. MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions (checked 2026-10-31)
  5. [5] MDN contributors. Array.prototype.reduce(). MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce (checked 2026-10-31)
  6. [6] MDN contributors. while. MDN Web Docs. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while (checked 2026-10-31)