Function Contracts

The Course
•
5 min read

Start with what you know

This course assumes you can use variables, branches, loops, arrays of records, functions, map/filter, basic tests, and a small HTML form. You do not need promises, modules, classes, or a framework yet. If you cannot build the beginner reading report without copying, revisit the independent beginner brief first. Needing a refresher is normal.

The new goal is a reading queue: search a catalogue, validate its results, save selected books, and keep the interface correct when requests overlap or fail. We will begin with small functions so each later moving part has a clear job. Most examples use deterministic local data; an unreliable public API should not decide whether you can finish a lesson.

Check the bridge from beginner

You already have the building blocks. These are the specific beginner skills we will reuse:

SkillWhere to refresh it
Return values, map, and filterBeginner functions and callbacks
Arrays of records and shallow copiesBeginner records and references
Throwing, catching, and checking resultsBeginner debugging and tests
A form changing state and updating textBeginner forms; needed again from intermediate Day 5

You do not need to recognize every new symbol on later pages. This course introduces destructuring, promises, modules, and request lifecycles before using them in the project.

Exercise

Readiness check: reuse your study-plan skills

Before learning the new book model, write openTopics(sessions). Return an array of topic names for incomplete sessions, without changing any records. Print Arrays and confirm the first original record is still done. You may use a loop or map/filter. If you need the solution, refresh the linked beginner explanation and try again with new values.

Readiness working space JavaScript
const sessions = [
  { topic: "Loops", done: true },
  { topic: "Arrays", done: false }
];
// Define openTopics, print its joined result, then inspect sessions[0].done.
Hint 1: the idea

Select the incomplete records before turning records into names.

Hint 2: a first step

Use a condition on done for selection and the topic field for the result. Neither step needs to assign to a record.

Show one solution
function openTopics(sessions) {
  const open = sessions.filter(session => !session.done);

  return open.map(session => session.topic);
}

const sessions = [
  { topic: "Loops", done: true },
  { topic: "Arrays", done: false }
];

console.log(openTopics(sessions).join(", "));
console.log(sessions[0].done);
Arrays
true

Try an empty list and an all-completed list. Both return an empty array. This is a readiness check for the familiar skills, not a test of material we have not taught yet.

Say what a function promises

A contract describes accepted inputs, returned outputs, changes to input, and failures. “Adds a book” is incomplete. “Given valid books with unique ids and one valid book, return a list containing that id once; create a new list only when adding it and preserve the input” is testable. An id is a stable string identifying one book, independent of its position or title.

The function below assumes its records are valid. Validation is a separate boundary we will add on Day 1. A duplicate id leaves the original list unchanged. Returning the same array in that case is intentional; a new array is only needed for a changed value.

Keep each editor self-contained. JavaScript console editors have no DOM or permanent files; the webpage labs and module examples explicitly use their own environments. Spend the proposed study time tracing and changing code, not only reading it.

Predict and trace

Write the output before running. Follow the values through each call; if your prediction differs, identify the first step that surprised you.

Predict, then run JavaScript
function saveOnce(saved, book) {
  if (saved.some(item => item.id === book.id)) {
    return saved;
  }

  return [...saved, { ...book }];
}

const first = [{ id: "js", title: "JavaScript Notes" }];
const next = saveOnce(first, { id: "web", title: "The Web" });

console.log(first.length);
console.log(next.length);
console.log(saveOnce(next, next[0]).length);
Check your prediction
1
2
2

some asks whether any id matches. The array and added record are copied; the existing records are shared because this function does not edit them.

Build it yourself

Exercise

Function Contracts: practise

Write hasBook(books, id), returning a boolean. Check an existing id, a missing id, and an empty list. Print the three answers.

Your working space JavaScript
const books = [{ id: "js", title: "Notes" }];
// Define hasBook and check three cases.
Hint 1: the idea

Search by stable id, not by the array position.

Hint 2: a first step

Use some with a callback comparing item.id to the id parameter.

Show one solution
function hasBook(books, id) {
  return books.some(book => book.id === id);
}

const books = [{ id: "js", title: "Notes" }];

console.log(hasBook(books, "js"));
console.log(hasBook(books, "missing"));
console.log(hasBook([], "js"));
true
false
false

Change the title while keeping the id. Membership should not change. State whether this helper validates malformed records or expects already-valid input.

Test a changed requirement

Change the title while keeping the id. Membership should not change. State whether this helper validates malformed records or expects already-valid input.

Explain before moving on

Explain why a title is a poor identity and which objects saveOnce copies. Describe one input your contract excludes.

Was this article helpful?

Your response is saved on this device.