cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Spread & rest

Learn how JavaScript’s three dots expand arrays, calls, and objects, collect leftover values with rest, and make safe shallow copies and immutable updates.

By the end, you’ll be able to
  • 01
    Read the direction of ...Tell when the dots expand values and when they collect leftovers.
  • 02
    Merge with confidencePredict array and object spread order, including which object property wins.
  • 03
    Copy without surprisesUse shallow copies and immutable updates, and know when structuredClone is safer.

Same dots, two directions

JavaScript uses ... for two opposite jobs. The name depends on where the dots appear: spread expands, and rest collects. You already met rest parameters and spread in calls in the Default & rest parameters lesson. Here we go deeper: arrays, calls, objects, destructuring, merge order, shallow copies, and the edge cases professionals trip over.

The one sentence definition

Spread expands one value into many positions; rest collects many leftover positions into one value.

Real-life analogyA bag of marbles

Imagine a pouch of marbles. Spread is pouring the pouch out so every marble stands alone on the table. Rest is sweeping the leftover marbles back into one pouch. Same three dots, opposite direction.

In real life: Pour a bag onto the table
In JavaScript: Spread: ...bag expands each item
In real life: Each marble now stands alone
In JavaScript: Array elements, call arguments, or object properties appear separately
In real life: Sweep leftover marbles back into one bag
In JavaScript: Rest: ...leftovers collects what was not already taken

Where the analogy stops: The marbles do not have keys, prototypes, getters, or references. JavaScript values do, so the rest of the lesson adds those rules.

Spread versus rest at a glance
Where ... appearsNameWhat happensTiny example
Array literalSpreadExpands an iterable into elements[0, ...items]
Function callSpreadExpands an iterable into argumentsfn(...args)
Object literalSpreadCopies own enumerable properties{ ...a, ...b }
Array destructuringRestCollects remaining elementsconst [x, ...xs] = list
Object destructuringRestCollects remaining own enumerable propertiesconst { id, ...rest } = obj
Function parametersRestCollects remaining argumentsfunction f(...args) {}

Try the first lab before reading further. Each card contains .... Your job is to decide whether those dots expand or collect.

Which dots are these?
  • const all = [0, ...numbers];
  • Math.max(...scores)
  • const merged = { ...defaults, ...user };
  • const [first, ...others] = list;
  • const { password, ...safeUser } = user;
  • function sum(...numbers) { }
Try it yourself
0 of 6 correct

Sort by direction: are the dots expanding a value, or collecting leftovers?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Spread in arrays & calls

STEP THROUGH

In an array literal, spread inserts the elements of an iterable at that exact position. It is a non-mutating way to copy, prepend, append, or insert. In a function call, spread turns an iterable into separate arguments.

Array literal

["intro", ...front, "quiz"] creates a new array. The old front array is not changed.

Function call

Math.max(...scores) calls Math.max with each score as its own argument, not with one array argument.

Step through the example. Then switch line 3 and notice that spread obeys order exactly: it does not sort, flatten deeply, or mutate the source arrays.

Pour arrays into arrays and calls
Step 0 of 7Ready
Your turn: follow the blue line

Predict the order in lesson, then step. Switch the setting and notice that spread keeps the source arrays but changes where their elements land.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const back = ["quiz"];const lesson = ["intro", ...front, "lab", ...back];console.log(lesson.join(" -> "));function announce(first, second, third) {  console.log(first + " | " + second + " | " + third);}announce(...lesson.slice(0, 3));
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Choose line 3: where should the arrays be poured?

Changing the line starts a fresh replay. Predict both printed lines again.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The Slicing & splicing lesson introduced concat and spread for array copying. Use spread when the position is easy to read at a glance, especially for small immutable updates.

Spread in objects

INTERACTIVE

In an object literal, spread copies own enumerable properties into a new object. The copy happens from left to right. If two sources name the same property, the later one overwrites the earlier one.

Real-life analogyStacking shopping lists

Think of each object as a shopping list. Start with defaults, then add the user's list. If both lists name theme, the newer note is the one JavaScript keeps.

In real life: The first shopping list
In JavaScript: defaults copied first
In real life: A newer shopping list
In JavaScript: userSettings copied later
In real life: The same item appears twice
In JavaScript: The same property key
In real life: The newer note wins
In JavaScript: Later property wins

Where the analogy stops: Real lists can show both notes. Object spread keeps only the last value for a key.

Layer object settings
Step 0 of 4Ready
Your turn: follow the blue line

Switch the merge order. The same two objects produce different winners because later properties overwrite earlier ones.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const userSettings = { theme: "dark" };const merged = { ...defaults, ...userSettings };console.log(merged.theme + " / " + merged.pageSize);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Choose the layer order on line 3.

Later properties win, so this setting changes the printed theme.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

This is why { ...user, name: "X" } and { name: "X", ...user } are not the same. In the first, name is definitely "X". In the second, user.name can overwrite it.

Object spread edge casesPop out in the code editor (opens in a new tab)JavaScript
const safe = { ...null, ...undefined };const letters = { ..."JS" };const source = Object.create({ inherited: "skip" });Object.defineProperty(source, "hidden", { value: "skip", enumerable: false });Object.defineProperty(source, "label", {  enumerable: true,  get() { return "Ada"; },});const copy = { ...source };console.log(JSON.stringify(safe));console.log(JSON.stringify(letters));console.log(JSON.stringify(copy));console.log([...null]);

The first three logs are {}, {"0":"J","1":"S"}, and {"label":"Ada"}. The final line throws a TypeError because array spread needs an iterable, and null is not iterable.

Object spread is not a magic clone

It copies own enumerable properties. It skips inherited and non-enumerable properties. If a copied property is a getter, the getter runs and the returned value becomes a plain value on the new object.

Rest in destructuring

STEP THROUGH

Destructuring pulls values out of arrays or objects. Rest in a destructuring pattern collects whatever was not already pulled out. This is the opposite direction from spread.

Sweep leftovers with rest
Step 0 of 6Ready
Your turn: follow the blue line

Decide which dots are collecting leftovers. Then step through the object and array rest patterns.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const { password, ...safeUser } = user;const steps = ["draft", "review", "publish"];const [first, ...others] = steps;console.log(JSON.stringify(safeUser));console.log(first + " / " + others.join(", "));
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The object pattern { password, ...safeUser } is useful in real code because it omits one field and keeps the rest. The array pattern [first, ...others] is useful when the first item has a special meaning and the remaining items should stay grouped.

Rest must be last

const [first, ...middle, last] = list; is a SyntaxError. Rest means “collect everything left,” so JavaScript does not allow another array or object binding after it in the same pattern.

Merging & copying

Spread creates a new outer container, but it is a shallow copy. That matches what you learned in the Objects & references lesson: object variables can share the same nested object.

Immutable nested updatePop out in the code editor (opens in a new tab)JavaScript
const state = {  user: { name: "Grace", role: "admin" },  tags: ["js", "data"]};const nextState = {  ...state,  user: { ...state.user, name: "Ada" },  tags: [...state.tags, "spread"]};console.log(state.user.name);console.log(nextState.user.name);console.log(state.tags === nextState.tags);

The important part is copying every container on the path you change. The outer state is copied, the nested user object is copied before name changes, and tags is copied before appending. This is the style used by UI state tools such as React and Redux because new references make changes easy to detect.

Choosing a copy tool
ToolDepthGood forWatch out
[...items]ShallowCopying or rebuilding arraysNested objects are shared
{ ...object }ShallowMerging options and updating stateLater properties overwrite earlier ones
Object.assign({}, a, b)ShallowOlder object merge styleAlso copies left to right; can mutate the first argument
structuredClone(value)Deep for supported valuesIndependent copies of structured dataFunctions, DOM nodes, and some special values are not cloneable

Where you’ll use this

Spread and rest appear anywhere you transform data without mutating the original. These patterns are small, but they show up constantly.

  • Merge defaults with user settings: { ...defaults, ...userSettings } keeps missing defaults and lets user choices win.
  • Remove private fields: const { password, ...safeUser } = user; keeps a clean object for display or logging.
  • Append immutably: [...items, newItem] returns a new array.
  • Insert or remove with slices: combine slice and spread to rebuild the parts you want.
Insert and remove with spread plus slicePop out in the code editor (opens in a new tab)JavaScript
const inserted = [  ...items.slice(0, index),  newItem,  ...items.slice(index)];const removed = [  ...items.slice(0, index),  ...items.slice(index + 1)];

Order rules & pitfalls

Most mistakes come from expecting the dots to do more than they promise. Keep these rules close:

  1. Array and call spread keep the iterable’s order.
  2. Object spread copies left to right; later properties win.
  3. Rest in destructuring must be last because it collects all remaining values.
  4. Object spread copies only own enumerable properties.
  5. Spread copies are O(n) because JavaScript visits the values it copies.
Performance and size

Copying an array or object with spread is proportional to the number of entries copied. Spreading very large arrays into function calls can hit engine argument limits and throw a RangeError. For huge data, prefer a loop, reduce, or an API that accepts an array.

Common misconceptions

“Spread and rest are the same thing.”

They use the same token, but the direction is opposite. Spread expands; rest collects. The syntax around the dots tells you which one you have.

“Object spread makes a deep copy.”

It makes a new outer object only. Nested objects and arrays are still shared unless you copy those levels too or use a deep-copy tool.

“Object spread copies every property.”

It copies own enumerable properties. Inherited and non-enumerable properties are skipped, and getters are invoked during the copy.

“Spreading null always throws.”

Object spread skips null and undefined, but array spread with them throws because arrays and calls need iterables.

“Spread is free because it looks short.”

The syntax is short; the work is still a copy. For normal UI data that is fine. For huge lists, measure and choose a tool deliberately.

Practice: spread & rest

5 EXERCISES

Predict first, then run the snippets in your console or editor. Each answer is checked against the real output.

Exercise 1 · Warm-upMerge defaults with user settings

What does the program print? Then run it to check.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const defaults = { theme: "light", density: "comfortable" };
const user = { density: "compact" };
const settings = { ...defaults, ...user };
console.log(settings.theme + " / " + settings.density);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeRemove the password field

    Use object rest to keep safe data and leave the secret out.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const user = { name: "Ada", password: "secret", role: "admin" };
    const { password, ...publicUser } = user;
    console.log(Object.keys(publicUser).join(","));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeUpdate a nested property immutably

      Predict the output and notice that the old nested name stays unchanged.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const state = { user: { name: "Grace", score: 10 }, theme: "dark" };
      const next = { ...state, user: { ...state.user, name: "Ada" } };
      console.log(state.user.name + " -> " + next.user.name);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticePredict merge order

        Enter both printed lines in order.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const user = { name: "Ada" };
        console.log(({ ...user, name: "Lin" }).name);
        console.log(({ name: "Lin", ...user }).name);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeAppend an item immutably

          Confirm that the old array and the new array print differently.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const items = ["draft", "review"];
          const nextItems = [...items, "publish"];
          console.log(items.join("/") + " -> " + nextItems.join("/"));

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS

            Every option explains itself. For code questions, read the dots from the surrounding syntax before you choose.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7Which sentence is the best shortcut for remembering the two meanings of ...?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does the array spread snippet print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const a = ["x", "y"];
              console.log([0, ...a, 3].join(""));

              Choose an answer to see the explanation.

            3. Question 3 of 7What does the object merge snippet print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const defaults = { theme: "light", size: 20 };
              const user = { theme: "dark" };
              console.log(({ ...defaults, ...user }).theme);

              Choose an answer to see the explanation.

            4. Question 4 of 7What does the object rest snippet print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const user = { name: "Ada", password: "secret", role: "admin" };
              const { password, ...safeUser } = user;
              console.log(Object.keys(safeUser).join("/"));

              Choose an answer to see the explanation.

            5. Question 5 of 7Which line is invalid JavaScript?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const [first, ...middle, last] = [1, 2, 3];

              Choose an answer to see the explanation.

            6. Question 6 of 7What does the shallow copy snippet print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const state = { user: { name: "Grace" } };
              const copy = { ...state };
              copy.user.name = "Ada";
              console.log(state.user.name);

              Choose an answer to see the explanation.

            7. Question 7 of 7Which statement about object spread is true?

              Choose an answer to see the explanation.

            Key takeaways

            • Spread expands; rest collects.
            • Array and call spread expand iterable values in order.
            • Object spread copies own enumerable properties, and later properties win.
            • Rest in destructuring collects leftovers and must be last.
            • Spread copies are shallow and O(n); copy nested levels deliberately.

            Remember the one-liner.
            Spread expands values out; rest collects leftovers back in.

            Up next: Map & Set, where you’ll choose collections made for unique values and flexible keys.

            CompleteFrontend Clear concepts. Working examples.