Skip to content
aviral gupta

// B4.1 · ~30 min · Beginner

Declaring and calling functions

After this lesson you declare a function with parameters, call it with arguments, know when it can be called before its line, and pass one function to another.

Lesson 1 of 5 in B4 Functions

Start of the module

You will be able to

  • Declare a function with parameters and call it with arguments, matched by position
  • Know that a function declaration is hoisted, while a const function expression cannot be called before its line
  • Pass a function as a value to another function, and read the "is not a function" error
  1. Warm-up · Activity 1 of 7

    Warm-up: a program declares function greet(name) { … }. Which line calls it?

  2. Predict · Activity 2 of 7

    Predict before you read on: what does this print?

    console.log("one");
    function sayTwo() {
      console.log("two");
    }
    console.log("three");
  3. Practice · Activity 3 of 7

    Fill in the call so that the program prints 49, the square of 7.

    function square(n) {
      return n * n;
    }
    console.log(____);
    console.log();
  4. Practice · Activity 4 of 7

    In function area(width, height) { return width * height; } and the line area(4, 5);, match each term to what it is.

  5. Practice · Activity 5 of 7

    The function is stored in a const, below the call. What happens?

    console.log(double(4));
    const double = function (n) {
      return n * 2;
    };
  6. Brain teaser · Activity 6 of 7

    Brain teaser. speak takes a function as its first argument. What does this print?

    function shout(text) {
      return text.toUpperCase() + "!";
    }
    function whisper(text) {
      return text.toLowerCase() + "...";
    }
    function speak(style, text) {
      return style(text);
    }
    console.log(speak(whisper, "HELLO"));
    console.log(speak(shout, "hi"));
  7. Apply · Activity 7 of 7

    Mini-task. Declare a function tip(bill, percent) that returns the tip: bill * percent / 100. Call it three times with different arguments and print each result with a label, such as Tip on 40 at 10%: 4. Then move one call above the declaration and run it again: it still works, because declarations are hoisted.

    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 small shop

Four functions for a shop: withVat adds 19% VAT, formatPrice turns an amount into text, roundUp is a function expression, and printEach takes a list of prices and a function to apply to each one. The first line calls two declarations before they appear: hoisting makes that work. Run it, then pass withVat to printEach instead and see the long decimals.

main.js

// A small shop: functions declared once, called many times.
console.log("Net 10 ->", formatPrice(withVat(10)));

function withVat(net) {
  return net * 1.19;
}

function formatPrice(amount) {
  return amount.toFixed(2) + " EUR";
}

const roundUp = function (amount) {
  return Math.ceil(amount);
};

function printEach(prices, change) {
  for (const p of prices) {
    console.log(p, "->", change(p));
  }
}

printEach([4.5, 20], formatPrice);
printEach([4.5, 20.2], roundUp);

Run it with

node main.js

Output

Net 10 -> 11.90 EUR
4.5 -> 4.50 EUR
20 -> 20.00 EUR
4.5 -> 5
20.2 -> 21
  • formatPrice(withVat(10)) runs the inner call first: its result, about 11.9, becomes the argument of formatPrice.
  • Line 2 calls withVat and formatPrice before their declarations, which works because declarations are hoisted.
  • printEach receives formatPrice and roundUp without parentheses, and calls each one as change(p).
  • roundUp is a const, so it is used only after its line has run.
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

Your first two functions

Complete greet(name) so that it returns "Hello, " followed by the name and "!": greet("Ada") gives Hello, Ada!. Complete rectangleArea(width, height) so that it returns width times height. Keep the word export in front of each function: it lets the tests on this page import and call your functions. Modules and export are taught in module I3.

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: both functions return undefined, because their bodies are empty.

  2. Hint 2

    Inside greet, join the parts with +: "Hello, " + name + "!".

  3. Hint 3

    Each body needs one line that starts with return, such as return width * height;.

Show a solution

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

// Return "Hello, " followed by the name and "!".
export function greet(name) {
  return "Hello, " + name + "!";
}

// Return the area of a rectangle: width times height.
export function rectangleArea(width, height) {
  return width * height;
}
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 "Hello, " followed by the name and "!".
export function greet(name) {
}

// Return the area of a rectangle: width times height.
export function rectangleArea(width, height) {
}

main.test.js

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

test('greet("Ada") returns Hello, Ada!', () => {
  assert.equal(greet('Ada'), 'Hello, Ada!', `greet("Ada") returned ${JSON.stringify(greet('Ada'))}, expected "Hello, Ada!"`);
});

test('greet works for any name', () => {
  assert.equal(greet('Linus'), 'Hello, Linus!', `greet("Linus") returned ${JSON.stringify(greet('Linus'))}, expected "Hello, Linus!"`);
});

test('rectangleArea(3, 4) returns 12', () => {
  assert.equal(rectangleArea(3, 4), 12, `rectangleArea(3, 4) returned ${rectangleArea(3, 4)}, expected 12`);
});

test('rectangleArea uses both arguments', () => {
  assert.equal(rectangleArea(5, 2), 10, `rectangleArea(5, 2) returned ${rectangleArea(5, 2)}, expected 10`);
});

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

Count with a function you are given

countWhere(items, check) should count the items for which check(item) is true. The tests pass a different check function each time, such as one that tells whether a number is above 5. The starter counts every item. Find out why, and fix it so that countWhere([3, 8, 12, 1], isBig) returns 2.

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 starter returns 4 for four numbers. Is if (check) ever false?

  2. Hint 2

    check holds a function, and a function is a truthy value. The if has to use what the function returns for this item.

  3. Hint 3

    Call it with the item: if (check(item)) { … }.

Show a solution

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

// Count the items for which check(item) is true.
export function countWhere(items, check) {
  let count = 0;
  for (const item of items) {
    if (check(item)) {
      count++;
    }
  }
  return count;
}
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

// Count the items for which check(item) is true.
export function countWhere(items, check) {
  let count = 0;
  for (const item of items) {
    if (check) {
      count++;
    }
  }
  return count;
}

main.test.js

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

const isBig = function (n) {
  return n > 5;
};

test('counts the numbers above 5', () => {
  const got = countWhere([3, 8, 12, 1], isBig);
  assert.equal(got, 2, `countWhere([3, 8, 12, 1], isBig) returned ${got}, expected 2`);
});

test('works with another check function', () => {
  const isEmpty = function (s) {
    return s === "";
  };
  const got = countWhere(["a", "", "b", ""], isEmpty);
  assert.equal(got, 2, `counting the empty strings returned ${got}, expected 2`);
});

test('gives 0 for an empty list', () => {
  const got = countWhere([], isBig);
  assert.equal(got, 0, `countWhere([], isBig) returned ${got}, expected 0`);
});

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

Calling a const function before its line

console.log(greet("Ada"));

const greet = function (name) {
  return "Hello, " + name;
};

What Node.js prints

ReferenceError: Cannot access 'greet' before initialization

Why, and the fix

Only function declarations are hoisted. greet is a const holding a function expression, and like any const it cannot be used before its line has run. Move the call below the definition, or write it as a declaration: function greet(name) { … }, which can be called from anywhere in the file.

Passing the result instead of the function

function double(n) {
  return n * 2;
}
function applyTo(value, change) {
  return change(value);
}
console.log(applyTo(5, double(5)));

What Node.js prints

TypeError: change is not a function

Why, and the fix

double(5) is called at once, so applyTo receives the number 10 in change, and change(value) tries to call a number. The message names the value that was called. Pass the function itself, without parentheses: applyTo(5, double).

A misspelled keyword

fuction greet(name) {
  return "Hi " + name;
}
console.log(greet("Bo"));

What Node.js prints

SyntaxError: Unexpected identifier 'greet'

Why, and the fix

fuction is not a keyword, so JavaScript reads it as a name, and a second name, greet, straight after it makes no sense. Because the file cannot be read, nothing runs at all. The message points at greet, but the fault is the word before it: write function.

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

Declare once, call many times

A function declaration is the keyword function, a name, parameters in parentheses and a body in curly braces: function square(number) { return number * number; }. Declaring it runs nothing yet. A call, the name followed by parentheses, runs the body: square(5). Parameters are the names in the definition; arguments are the values in the call, matched by position, so in square(5) the parameter number receives 5. return hands a value back, and the call becomes that value: console.log(square(5)) prints 25. Lesson B4.3 looks at return closely. Call a function as often as you need, with different arguments each time.

Declarations are hoisted, expressions are not

JavaScript sets up every function declaration before it runs the first line, so you can call a declared function above the line that defines it. This is called hoisting. A function can also be written as an expression and stored in a constant: const double = function (n) { return n * 2; };. That constant exists only once its line has run. Calling double above it stops the program with ReferenceError: Cannot access 'double' before initialization, the same error as reading any const too early. Declarations suit helpers you want to use anywhere in a file; an expression must be defined before you call it.

Functions are values

A function is a value, like a number or a string. You can store it in a constant and pass it to another function as an argument: printEach(prices, formatPrice). Without parentheses you pass the function itself; formatPrice(4.5) would call it at once and pass only its result. The receiving function calls it through its parameter: change(p). If the value passed is not a function, that call fails with TypeError: change is not a function. The same TypeError appears when you call a number or a misspelled method. The name in the message is the value that was called.

Sources

Last reviewed October 3, 2026