What you'll be able to do

By the end of this page you should be able to create an array, read and change items by index, use length, add/remove with push / pop, and explain why list2 = list1 does not copy the list — both names point at the same array.

Previous post: errors — try, catch, throw.

Who this is for

  • People who store several values in a, b, c and wish for one list
  • Beginners who write list2 = list1 and wonder why both lists change after push
  • Anyone who has seen undefined when reading past the end of an array

You can skip this if you already use indexes safely, know push mutates, and copy with slice or spread when you need a second list. Come back when a “copy” still shares data.

An array is an ordered list

An array holds values in order. The first item is at index 0, the second at 1, and so on.

const tags = ["javascript", "tutorials", "developers"];
console.log(tags[0]); // "javascript"
console.log(tags.length); // 3

Scenario A — a tag list on a Learn post. One array, several strings, easy to loop later.

Scenario B — scores for one quiz. Numbers in order: [8, 10, 7]. Same idea: position matters.

Prefer the literal [...] when you start. You rarely need new Array(3) as a beginner — that form is easy to misuse.

Read and write by index

const colors = ["red", "green", "blue"];
console.log(colors[1]); // "green"
colors[1] = "teal";
console.log(colors); // ["red", "teal", "blue"]

Scenario A — fix a typo in place. Change colors[1] and the array updates.

Scenario B — reading past the end. colors[99] is undefined — not a crash. Check index < colors.length when the index comes from user input or a loop.

length is the count of items. The last index is length - 1 when the array is not empty.

push and pop change the same array

push adds to the end. pop removes from the end. Both mutate the array (they change it in place).

const queue = ["A", "B"];
queue.push("C");
console.log(queue); // ["A", "B", "C"]
const last = queue.pop();
console.log(last); // "C"
console.log(queue); // ["A", "B"]

Scenario A — a to-do list in memory. push a new task; pop if you only care about the last one (stack style).

Scenario B — logging what changed. After push, every variable that points at queue sees the new length. That is useful — and dangerous when you thought you had a private copy.

We covered pure vs impure earlier: mutating an array you share is a side effect on shared state.

The big surprise: list2 = list1 shares one array

const list1 = ["x", "y"];
const list2 = list1; // same array, two names
list2.push("z");
console.log(list1); // ["x", "y", "z"] — changed too

Scenario A — “backup” that is not a backup. You assign backup = cart, then cart.push(item), and backup grows. Both names were the same cart.

Scenario B — a function that receives an array. If the function pushes, the caller’s array changes unless you copied first.

Rule: assignment copies the reference (the pointer), not a new list of values.

How to get a real second list (shallow copy)

const list1 = ["x", "y"];
const list2 = list1.slice(); // new array, same item values
// or: const list2 = [...list1];
list2.push("z");
console.log(list1); // ["x", "y"]
console.log(list2); // ["x", "y", "z"]

Scenario A — experiment without breaking the original. Copy, then mutate the copy.

Scenario B — nested objects inside the array. slice / spread copy the outer list only. Inner objects are still shared. That deeper story comes later with objects and shallow vs deep copy — for now, know that string/number items are safe to treat as independent after a shallow copy.

const arrays can still mutate

const nums = [1, 2];
nums.push(3); // allowed
// nums = [9]; // would throw — rebinding the name

Scenario A — const means the binding stays. The variable always points at that array object.

Scenario B — people expect const to freeze contents. It does not. Use const for the name; use copy-or-don’t-mutate habits for the contents.

A tiny practice file

Save as arrays.html. Add tags; see shared vs copied.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Arrays</title>
  </head>
  <body>
    <input id="tag" placeholder="New tag" />
    <button id="addShared">Push on shared copy</button>
    <button id="addCopied">Push on real copy</button>
    <pre id="out"></pre>
    <script>
      const original = ["javascript"];
      const shared = original;
      const copied = original.slice();

      function show() {
        document.getElementById("out").textContent =
          "original: " + JSON.stringify(original) + "\n" +
          "shared:   " + JSON.stringify(shared) + "\n" +
          "copied:   " + JSON.stringify(copied);
      }

      document.getElementById("addShared").addEventListener("click", function () {
        const t = document.getElementById("tag").value.trim() || "item";
        shared.push(t);
        show();
      });

      document.getElementById("addCopied").addEventListener("click", function () {
        const t = document.getElementById("tag").value.trim() || "item";
        copied.push(t);
        show();
      });

      show();
    </script>
  </body>
</html>

Click “shared” a few times — original grows too. Click “copied” — only copied grows.

Mistakes I see a lot

1. list2 = list1 as a copy. It is not. Use slice or spread.

2. Off-by-one on the last item. Last index is length - 1, not length.

3. Assuming missing indexes throw. Out-of-range reads give undefined.

4. Thinking const freezes the array. Contents can still change.

5. Using new Array(5) for five empty slots. Prefer [1, 2, 3] literals until you know why sparse arrays exist.

6. Mutating a shared array inside a helper and returning it. Callers keep a surprise reference — same theme as impure functions.

What to try before the next post

  1. Build ["a","b","c"], log index 0 and length.
  2. push one item; pop it back.
  3. Assign b = a, push on b, log a.
  4. Repeat with b = a.slice() and compare.
  5. Build the tiny HTML file.

Next in this series: forEach and map — when to transform into a new list vs when to only run a side effect.

Try this next outside the series

Arrays get easier when you see them render UI lists and move through real transforms.