Structured data: arrays and objects · Lesson 22 of 77 · concept

Arrays: many values under one name

About 55 minutes.

By the end of this lesson you can

  • Say why a list of values needs one name plus a position, not one variable per value.
  • Write an array literal and read any element out of it with square brackets.
  • Explain why the first element is at index 0 and the last is at index length - 1.
  • Predict what reading a position that does not exist gives you, and why it is not an error.
  • Use at(-1) to read from the end without doing the arithmetic yourself.

Why one name per value stops working

Why it matters

Everything you have written so far gave one name to one value. `const first = 'Ada'` holds a single piece of text; `const total = 12` holds a single number. That works right up until you have three of something, and it falls apart completely at three hundred.

Suppose you are storing the stops on a train route. You could invent `stop1`, `stop2`, `stop3` and keep going — but you would have to know how many stops there are before you wrote the program, and every function you wrote would have to name each variable separately. Add a stop and you edit the whole file.

What you actually need is a container: one name for the whole group, plus a way to say which member of the group you mean. JavaScript's container for an ordered group is called an array. This lesson is only about getting values into one and back out again. Nothing else.

What an array is

Explanation

An array is a single value that holds other values in a fixed order. You write one with square brackets, and commas between the items: `const scores = [10, 8, 9];`. That is one statement creating one value. `scores` is not three variables — it is one variable whose value happens to contain three numbers, in that order.

The values inside are called elements. They can be any kind of value you have already met: numbers, strings, booleans, `null`, `undefined`, and later on other arrays and objects. They do not have to match each other. `[1, 'two', true]` is a perfectly legal array, though mixing types like that usually means you have not yet decided what the list represents.

The order you write them in is the order they stay in. An array is not a bag you tip values into; it is a numbered row of slots.

Positions start at zero

A mental model

To read one element you write the array's name followed by a number in square brackets: `scores[0]`. That number is the index, and this is the part that catches every beginner exactly once — the first element is at index 0, not 1.

The mental model that makes it stick is distance from the start, not place in a queue. Index 0 means zero steps along from the beginning, which is the first element, because you have not moved yet. Index 2 means two steps along, which lands on the third element. Think of house numbers measured in metres from the corner rather than counted first, second, third.

So for `const scores = [10, 8, 9]`: `scores[0]` is 10, `scores[1]` is 8, and `scores[2]` is 9. There is no `scores[3]` — you have run out of steps.

length, and the last index

Explanation

Every array knows how many elements it holds. You read that with `.length` — no parentheses, because it is a property of the array and not a function you call. For `[10, 8, 9]`, `scores.length` is 3.

MDN defines `length` as a number that is always greater than the highest index in the array. That one sentence gives you the formula you will use constantly: because indexes start at 0, the last element sits at `length - 1`. A three-element array has `length` 3 and a last index of 2.

Write it as `list[list.length - 1]` rather than typing the number yourself. `scores[2]` is correct today and silently wrong the moment the array gains or loses an element. `scores[scores.length - 1]` stays correct forever, which is the whole reason `length` exists.

Reading past the end is not an error

A common mistake

Reading an index that does not exist does not stop your program. JavaScript hands you `undefined` and carries on. `scores[3]` is `undefined`, and so is `scores[99]`.

This is worth dwelling on, because it is the most common source of baffling array bugs. The program does not fail where the mistake is. It fails much later, somewhere that looks unrelated, when that `undefined` is finally used for something — `undefined + 1` is `NaN`, and reading a property off `undefined` throws a TypeError several lines further down. When you see `NaN` or a TypeError about `undefined`, suspect an index that went off the end.

The other half of the same mistake: `scores[-1]` is not the last element. Negative numbers in square brackets are not positions at all, so you get `undefined` there too.

Reading from the end with at()

Explanation

Because `list[list.length - 1]` is tedious to write and easy to get wrong, arrays also have an `at()` method. `scores.at(-1)` is the last element and `scores.at(-2)` the one before it. A non-negative index behaves exactly like square brackets, so `scores.at(0)` is the first element.

MDN states that `at()` returns `undefined` if the index is below `-array.length` or at or above `array.length`, so it fails the same gentle way square brackets do — no error, just `undefined`. Reach for `at()` when you are counting from the end, and square brackets the rest of the time.

Recap

Recap

An array is one value holding an ordered row of values. Square brackets with a number read one of them. Indexes count from zero, so the last element is at `length - 1`, or more simply `at(-1)`. Reading a position that is not there gives `undefined` instead of stopping the program — which is exactly why array bugs tend to surface late, and somewhere else.

Worked examples

Every output below was produced by actually running the code in the same sandbox your exercises run in — it is not a prediction.

Reading elements out of an array

Create one array and read three different things from it: the first element, the last element, and a position that does not exist. Watch what the last one gives you.

1const stops = ["Kings Cross", "Angel", "Old Street"];2 3console.log(stops[0]);4console.log(stops[2]);5console.log(stops.length);6console.log(stops[3]);
  • line 1

    One statement, one value. `stops` holds three strings in the order written.

  • line 3

    Index 0 is zero steps from the start — the first element.

  • line 4

    Index 2 is two steps along, so the third element, which is the last.

  • line 5

    `length` is a property, so no parentheses. It counts elements; it is not the last index.

  • line 6

    Index 3 is one step past the end. No error is thrown — the value is `undefined`.

What it prints

Kings CrossOld Street3undefined

The last element, two ways (and one that does not work)

Compare the arithmetic form with `at(-1)`, and see that square brackets do not understand negative positions.

1const stops = ["Kings Cross", "Angel", "Old Street"];2 3console.log(stops[stops.length - 1]);4console.log(stops.at(-1));5console.log(stops[-1]);
  • line 3

    `length` is 3, so `length - 1` is 2 — the last index. Recomputed every time, so it survives the array changing size.

  • line 4

    `at(-1)` means one back from the end. Same element, none of the arithmetic.

  • line 5

    Square brackets do not count backwards. This is `undefined`, not the last element.

What it prints

Old StreetOld Streetundefined

Why an off-the-end read bites you later

Show that the bad value produced by a bad index does not announce itself. It travels quietly into the next calculation.

1const prices = [4, 6];2const third = prices[2];3 4console.log(third);5console.log(Number.isNaN(third + 1));6console.log(prices.length);
  • line 2

    There is no third price. Nothing throws here — `third` is simply `undefined`.

  • line 4

    The mistake is invisible at this point: it just prints `undefined`.

  • line 5

    `undefined + 1` is NaN, JavaScript's this-arithmetic-did-not-work value. `Number.isNaN` asks whether that happened.

  • line 6

    Reading from an array never changes it. The length is still 2.

What it prints

undefinedtrue2

Check yourself

Not scored, not stored. Getting one wrong is the useful part.

Given `const days = ['Mon', 'Tue', 'Wed', 'Thu'];` — which expression gives you 'Thu'?

What does this print?

const letters = ["a", "b"];console.log(letters[1]);console.log(letters[2]);

An array has a length of 6. Which statement is true?

What does this print?

const queue = ["ann", "bo", "cy"];console.log(queue.at(-1));console.log(queue[-1]);

Write it yourself

Exercise · intro · 8 tests

Read a route by position

The `stops` array below lists a train route in order. Finish the three functions so they read from it correctly. `firstStop()` is written for you and returns the first stop. `lastStop()` must return the final stop, worked out from the array rather than typed in as a number. `stopAt(position)` takes a human-facing position — passengers call the first stop number 1 — and returns the stop at that position, or undefined when there is no such stop.

What your code must do

lastStop() must still return the final stop if `stops` gains or loses elements, so it cannot hardcode an index. stopAt(1) is the first stop and stopAt(stops.length) is the last. A position with no stop returns undefined; nothing throws.

Define stops, firstStop, lastStop and stopAt at the top level of your code — the tests call them by name.

Runs on your device in a sandbox with no network and no access to this page.

Exercise · core · 9 tests

Safe reads from any list

Write three small helpers that work on any array handed to them, including an empty one. `countOf(list)` returns how many elements the list has. `lastOf(list)` returns the last element, or undefined when the list is empty. `itemOr(list, index, fallback)` returns the element at that index, or `fallback` when that position does not exist.

What your code must do

None of the three may throw, whatever they are given. itemOr returns fallback only when the position is outside the array — an element that is genuinely null, 0, '' or false must come back unchanged, and a fallback of 0 must be returned as 0.

Define countOf, lastOf and itemOr at the top level of your code — the tests call them by name.

Runs on your device in a sandbox with no network and no access to this page.

Worth remembering

  • In an array of length N, what is the index of the last element?

    N - 1. Indexes count from 0, so a four-element array has indexes 0, 1, 2, 3. Reach it with list[list.length - 1] or list.at(-1).

  • What happens when you read an array index that does not exist?

    You get undefined, and nothing is thrown. The program carries on, so the failure usually appears later and somewhere else.

  • What is list[-1]?

    undefined. Square brackets do not count backwards. Use list.at(-1) for the last element.

Check this lesson against the source

We wrote the explanation; we did not invent the facts. This is the page that backs them.

MDN — Array: length

Check there: The sentence stating that length is always numerically greater than the highest index in the array — the rule behind reading the last element as list[list.length - 1].

MDN — Array.prototype.at()

Check there: That at() always returns undefined if index is below -array.length or at or above array.length.

Prefer to read the source in a structured breakdown? Turn this doc into a breakdown

0 of 2 exercises in this lesson solved on this device

Consent version 2026-07-31.1

Cookie preferences