Skip to content
aviral gupta

// B4.3 · ~30 min · Beginner

Return values

After this lesson you return results instead of printing them, stop a function early when its input is bad, and hand back several values at once.

Lesson 3 of 5 in B4 Functions

You will be able to

  • Return a value from a function and use the call as that value, and tell returning from printing
  • Know that a function without return gives undefined, and guard bad input with an early return
  • Avoid a line break straight after return, and return an array or object to give back several values
  1. Warm-up · Activity 1 of 7

    Warm-up: which keyword hands a value back from a function to the line that called it?

  2. Predict · Activity 2 of 7

    Predict before you read on: one function prints, the other returns. What does the program print?

    function addLog(a, b) {
      console.log(a + b);
    }
    function addReturn(a, b) {
      return a + b;
    }
    const x = addLog(2, 3);
    const y = addReturn(2, 3);
    console.log(x, y);
  3. Practice · Activity 3 of 7

    Fill in the keyword so that double hands its result back and the program prints 42.

    function double(n) {
      ____ n * 2;
    }
    console.log(double(21));
    n * 2;
  4. Practice · Activity 4 of 7

    Each return ends the function. What does this print?

    function ticketPrice(age) {
      if (age < 6) {
        return 0;
      }
      if (age < 18) {
        return 5;
      }
      return 9;
    }
    console.log(ticketPrice(4), ticketPrice(12), ticketPrice(40));
  5. Practice · Activity 5 of 7

    Each line is the body of a function. Match it to what console.log prints for a call of that function.

  6. Brain teaser · Activity 6 of 7

    Brain teaser. The value is on the next line, nicely indented. What does this print?

    function getTotal(a, b) {
      return
        a + b;
    }
    console.log(getTotal(2, 3));
  7. Apply · Activity 7 of 7

    Mini-task. Write minMax(numbers) that returns the smallest and the largest number as an array, [low, high]. Guard the empty list first: return [] straight away. Print minMax([7, 2, 9, 4]), minMax([5]) and minMax([]).

    Check your work against this list

Build it yourself

Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.

Worked example

A checkout

Four small functions build a receipt. subtotal returns a sum, discount uses early returns as guards, and priceRange returns two values in an array. logLine prints instead of returning: watch what its call gives on the last line. Run it, then change the code to "save5" in the first discount call and predict the new total.

main.js

// Small functions that return values, combined into a receipt.
function subtotal(prices) {
  let sum = 0;
  for (const p of prices) {
    sum += p;
  }
  return sum;
}

function discount(amount, code) {
  if (code !== "SAVE5") {
    return 0;
  }
  if (amount < 20) {
    return 0;
  }
  return 5;
}

function priceRange(prices) {
  let low = prices[0];
  let high = prices[0];
  for (const p of prices) {
    if (p < low) {
      low = p;
    }
    if (p > high) {
      high = p;
    }
  }
  return [low, high];
}

function logLine(text) {
  console.log("> " + text);
}

const prices = [12, 4.5, 20];
const sum = subtotal(prices);
const off = discount(sum, "SAVE5");
console.log("subtotal", sum);
console.log("discount", off, "and with a wrong code", discount(sum, "save5"));
console.log("to pay", (sum - off).toFixed(2));
console.log("range", priceRange(prices));
console.log("logLine gives", logLine("printed, not returned"));

Run it with

node main.js

Output

subtotal 36.5
discount 5 and with a wrong code 0
to pay 31.50
range [ 4.5, 20 ]
> printed, not returned
logLine gives undefined
  • The results of subtotal and discount are stored in constants and used again in later lines.
  • discount returns 0 as soon as one test fails; return 5 is reached only when both pass.
  • priceRange hands back two numbers in one array.
  • logLine prints its line first, while its argument is worked out; the call itself is undefined.
Change it and run it

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Exercises

Exercise 1 of 2

Return, do not print

fullName(first, last) should return a name such as Ada Lovelace, and initials(first, last) should return initials such as A.L.. The starter prints the right text, but the tests receive undefined. Change both functions so that they return their text instead of printing it.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    Run the tests: the text appears in the output, but each function returns undefined.

  2. Hint 2

    console.log shows a value; return hands it to the caller.

  3. Hint 3

    Replace console.log( … ) with return …, and drop the closing bracket of console.log.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

// Return the full name, such as "Ada Lovelace".
export function fullName(first, last) {
  return first + " " + last;
}

// Return the initials, such as "A.L.".
export function initials(first, last) {
  return first[0] + "." + last[0] + ".";
}
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

// Return the full name, such as "Ada Lovelace".
export function fullName(first, last) {
  console.log(first + " " + last);
}

// Return the initials, such as "A.L.".
export function initials(first, last) {
  console.log(first[0] + "." + last[0] + ".");
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {fullName, initials} from './main.js';

test('fullName returns the full name', () => {
  const got = fullName('Ada', 'Lovelace');
  assert.equal(got, 'Ada Lovelace', `fullName("Ada", "Lovelace") returned ${JSON.stringify(got)}, expected "Ada Lovelace"`);
});

test('fullName works for any name', () => {
  const got = fullName('Grace', 'Hopper');
  assert.equal(got, 'Grace Hopper', `fullName("Grace", "Hopper") returned ${JSON.stringify(got)}, expected "Grace Hopper"`);
});

test('initials returns the initials', () => {
  const got = initials('Ada', 'Lovelace');
  assert.equal(got, 'A.L.', `initials("Ada", "Lovelace") returned ${JSON.stringify(got)}, expected "A.L."`);
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Exercise 2 of 2

Guard the shipping cost

shippingCost(weight) returns 5 for a parcel up to 2 kg, 9 up to 10 kg and 15 above that. Anything that is not a number above 0 must return "invalid weight". The starter has no guard yet, and one of its returns hands back undefined. Add the guard as an early return at the top and fix the broken return.

Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.

The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.

Hints
  1. Hint 1

    Run the tests: shippingCost(1) returns undefined. Look at the line after the first return.

  2. Hint 2

    A line break straight after return ends the statement. Write return 5; on one line.

  3. Hint 3

    For the guard, put if (typeof weight !== "number" || weight <= 0) { return "invalid weight"; } as the first lines of the body.

Show a solution

One way to solve it. Yours can look different and still pass the checks.

export function shippingCost(weight) {
  // Return "invalid weight" for anything that is not a number above 0.
  if (typeof weight !== "number" || weight <= 0) {
    return "invalid weight";
  }
  if (weight <= 2) {
    return 5;
  }
  if (weight <= 10) {
    return 9;
  }
  return 15;
}
Run it on your computer

Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.

main.js

export function shippingCost(weight) {
  // Return "invalid weight" for anything that is not a number above 0.
  if (weight <= 2) {
    return
      5;
  }
  if (weight <= 10) {
    return 9;
  }
  return 15;
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {shippingCost} from './main.js';

test('a parcel up to 2 kg costs 5', () => {
  assert.equal(shippingCost(1), 5, `shippingCost(1) returned ${JSON.stringify(shippingCost(1))}, expected 5`);
  assert.equal(shippingCost(2), 5, `shippingCost(2) returned ${JSON.stringify(shippingCost(2))}, expected 5`);
});

test('a parcel up to 10 kg costs 9', () => {
  assert.equal(shippingCost(7), 9, `shippingCost(7) returned ${JSON.stringify(shippingCost(7))}, expected 9`);
});

test('a heavier parcel costs 15', () => {
  assert.equal(shippingCost(25), 15, `shippingCost(25) returned ${JSON.stringify(shippingCost(25))}, expected 15`);
});

test('0 and negative weights are invalid', () => {
  assert.equal(shippingCost(0), 'invalid weight', `shippingCost(0) returned ${JSON.stringify(shippingCost(0))}, expected "invalid weight"`);
  assert.equal(shippingCost(-3), 'invalid weight', `shippingCost(-3) returned ${JSON.stringify(shippingCost(-3))}, expected "invalid weight"`);
});

test('a weight that is not a number is invalid', () => {
  assert.equal(shippingCost('3'), 'invalid weight', `shippingCost("3") returned ${JSON.stringify(shippingCost('3'))}, expected "invalid weight"`);
});

package.json

{
  "type": "module"
}

package.json tells Node.js that the .js files are modules; keep it in the folder.

Run the program:

node main.js

Run the checks (needs learnrun.js in the same folder):

node --test
Download learnrun.js

Common mistakes

A line break after return

function total(a, b) {
  return
    a + b;
}
console.log(total(2, 3).toFixed(2));

What Node.js prints

TypeError: Cannot read properties of undefined (reading 'toFixed')

Why, and the fix

JavaScript ends the return statement at the line break, so total returns undefined and a + b never runs. The error appears only later, where the caller uses the result. Keep the value on the return line: return a + b;. A long value can start with an opening bracket on that line: return ( … );.

Printing instead of returning

function greeting(name) {
  console.log("Hello, " + name);
}
const text = greeting("Ada");
console.log(text.toUpperCase());

What Node.js prints

TypeError: Cannot read properties of undefined (reading 'toUpperCase')

Why, and the fix

greeting prints its line, so the program looks right at first, but it has no return, so text is undefined. Return the value, return "Hello, " + name;, and let the caller print it when it needs to.

return outside a function

const stock = 0;
if (stock === 0) {
  console.log("sold out");
  return;
}
console.log("in stock");

What Node.js prints

SyntaxError: Illegal return statement

Why, and the fix

return belongs inside a function body. At the top level of a file it is a SyntaxError, so the file cannot be read and not even sold out is printed. Use else for the other branch, or put the code in a function and return from that.

JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source

Exit ticket

5 questions, no hints. Score 80% or more to complete the lesson.

Finish every activity above to unlock the exit ticket.

Report a problem

Spotted something wrong or unclear? Say what, and it will be checked and fixed.

#

At least 20 characters.

Only if you want a reply.

Key ideas

return hands a value back

return ends the function at once and hands a value back to the caller: the call becomes that value. So const total = subtotal(prices) stores the result, and a call fits anywhere a value does: subtotal(prices) * 2, or as the argument of another call. Printing is different. console.log inside a function shows text in the console, but the caller gets nothing it can use: the call is undefined. Let functions return their results and let the caller decide whether to print them. That makes them reusable, and tests can check what they return.

undefined, and returning early

A function without return, or with a bare return;, gives undefined: the body runs, and the call is undefined. Because return ends the function at once, it makes a good guard. Check bad input at the top and return straight away: if (weight <= 0) { return "invalid weight"; }. Everything below the guard handles good input only, with no else and no extra nesting. Several returns in one function are normal: the first one that runs wins, and the lines after it never run in that call.

Several values, and the line break trap

A function returns one value. To hand back several, return an array or an object: return [low, high]; or return {min: low, max: high};. The caller reads them as result[0] or result.min. Objects and shorter ways to unpack them come in module B5; here they are only a preview. One trap: never break the line straight after return. JavaScript ends the statement there, so return on its own line followed by a + b; returns undefined and never works out a + b. Keep the value, or at least its opening bracket, on the return line.

Sources

Last reviewed October 3, 2026