completefrontend
Open lab My progress
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
    Tell spread from restLook at where ... stands and say whether it lays items out (spread) or gathers leftovers (rest).
  • 02
    Guess the result of a mergeSay what order spread puts items in, and which value wins when two objects have the same property.
  • 03
    Copy without surprisesKnow that spread copies only the outer level, copy inner levels when you need to, and know when structuredClone is the better tool.

Same dots, two directions

JavaScript uses three dots, ..., for two opposite jobs. Where the dots stand decides which job they do:

  • Spread takes the items out of a list and lays them out one by one.
  • Rest gathers the items that are left over into one new list.

You already met rest parameters and spread in function calls in the Default & rest parameters lesson. This lesson uses the dots in more places: arrays, function calls, objects, and destructuring. It also shows which value wins when two objects are merged, why a spread copy can still share data, and the mistakes people make most often.

The one sentence definition

Spread takes one value apart into many separate items. Rest gathers many leftover items into one value.

The same dots, used both waysPop out in the code editor (opens in a new tab)JavaScript
const items = ["tea", "toast"];
const menu = [...items, "fruit"];
const [first, ...leftovers] = menu;
console.log(menu.join(" / ") + " | " + first + " | " + leftovers.join(" / "));

Line 1 makes a small array, items. Line 2 uses spread while it builds a new array, menu. The two items from items are laid out inside menu, and "fruit" goes after them.

Line 3 uses rest while it reads menu. The variable first gets the first item. The variable leftovers gathers all the other items into a new array. Line 4 prints tea / toast / fruit | tea | toast / fruit.

Real-life analogyA bag of marbles

Picture a bag of marbles. Spread is like pouring the bag out, so every marble sits on its own on the table. Rest is like sweeping the leftover marbles back into one bag. The dots are the same; the direction is opposite.

In real life: Pour the bag out onto the table
In JavaScript: Spread: ...bag lays out each item
In real life: Each marble now sits on its own
In JavaScript: Array items, function arguments, or object properties appear one by one
In real life: Sweep the leftover marbles back into one bag
In JavaScript: Rest: ...leftovers gathers what was not already taken

The table lists every place the dots can stand. Read a row from left to right: where the dots are, which job they do, what happens, and a short example.

Spread and rest, side by side
Where are the dots?Which job?What happens?Short example
In the square brackets of a new array.SpreadThe items are laid out in the new array.[0, ...items]
In the round brackets of a function call.SpreadEach item becomes its own argument.fn(...args)
In the curly brackets of a new object.SpreadThe object's own properties are copied in.{ ...a, ...b }
In an array pattern, left of =.RestThe items that are left go into a new array.const [x, ...xs] = list
In an object pattern, left of =.RestThe properties that are left go into a new object.const { id, ...rest } = obj
In a function's parameter list.RestThe arguments that are left go into a new array.function f(...args) {}

Try the first lab now. Each card has ... in it. Decide whether those dots lay items out (spread) or gather leftovers (rest).

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 each card: do the dots lay items out, or gather the 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

Spread works in two places with lists:

  • Inside the square brackets of a new array, spread puts the items of another array at that exact spot. The old array does not change.
  • Inside the round brackets of a function call, spread hands each item to the function as its own argument. An argument is a value you pass into a function.

In a new array

["intro", ...front, "quiz"] makes a new array. The old front array stays the same.

In a function call

Math.max(...scores) gives Math.max each score as its own argument. It does not pass the whole array as one argument.

Put an array's items in the middlePop out in the code editor (opens in a new tab)JavaScript
const morning = ["tea"];
const breakfast = ["water", ...morning, "toast"];
console.log(breakfast.join(" + "));

Line 1 makes an array with one drink in it. Line 2 makes a new array. It puts the items of morning between two fixed values, so line 3 prints water + tea + toast. The morning array still has its one item. Spread fills the new array; it does not change the old one.

Spread in an array. A box called morning holds the array ["tea"]. An arrow takes the item "tea" out of the box and puts it between "water" and "toast" in the new array, breakfast. The morning box stays the same....morning["tea"]morningstays the same"water""tea""toast"the new array, breakfast
...morning takes the items out of the morning array and puts them at the spot of the dots. The new array gets the item "tea", not the whole array. morning does not change.
Spread a string into lettersPop out in the code editor (opens in a new tab)JavaScript
const word = "tea";
const letters = [...word];
console.log(letters.join("-"));

Spread in an array works on any iterable. An iterable is a value that JavaScript can walk through one item at a time. Arrays are iterable. Strings are iterable too: each character is one item. So line 2 makes an array of three letters, and line 3 prints t-e-a.

A plain object, like { theme: "dark" }, is not iterable. To copy an object's properties, use object spread, which comes in the next section.

Now step through a longer example. It spreads two arrays into a new one, then spreads part of that array into a function call. After that, switch line 3 and run it again. Notice that spread keeps the items in the same order. It does not sort them, and it does not change the old arrays.

Spread arrays into a new array and a call
Step 0 of 7Ready
Your turn: follow the blue line

Guess the order of the items in lesson, then step. Switch line 3 and run again: the old arrays stay the same, but their items land in different spots.

Running in
  1. script
Next: line 1
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));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose line 3: where should the items of the two arrays go?

Changing line 3 starts the steps again. Guess both printed lines before you step.

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 showed concat and spread for copying arrays. Spread is a good choice when you want to see at a glance where each item goes.

Spread in objects

INTERACTIVE

Inside the curly brackets of a new object, spread copies properties from another object into the new one. It copies the object's own enumerable properties:

  • Own means the property is stored on the object itself, not borrowed from another object.
  • Enumerable means the property shows up when you list the object's keys.
  • The properties you write in an object literal, like { theme: "dark" }, are both.

Spread copies from left to right. If two objects have a property with the same name, the one copied later replaces the one copied earlier.

Real-life analogyStacking notes on a list

Think of each object as a shopping list. You start with the old list, then read the newer one. If both lists mention theme, you follow the newer note.

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 is on both lists
In JavaScript: The same property name in both objects
In real life: You follow the newer note
In JavaScript: The later property wins
The later value winsPop out in the code editor (opens in a new tab)JavaScript
const defaults = { theme: "light", fontSize: 14 };
const user = { theme: "dark" };
const settings = { ...defaults, ...user };
console.log(settings.theme + " / " + settings.fontSize);

Line 1 sets two default values. Line 2 sets only a theme. Line 3 copies defaults first and user second. Both have a theme, so the later one, "dark", wins. Only defaults has fontSize, so it stays 14. Line 4 prints dark / 14.

Object spread, { ...defaults, ...user }. Row 1, defaults: theme "light" and fontSize 14. Row 2, user: theme "dark". The result, settings, has theme "dark", which came from user, and fontSize 14, which came from defaults.{ ...defaults, ...user }1. defaultstheme: "light"fontSize: 142. usertheme: "dark"later winsfrom defaultssettingstheme: "dark"fontSize: 14
JavaScript copies defaults first and user second. Both have a theme, so the later one, "dark", wins. Only defaults has fontSize, so it stays 14.

Step through the same idea below. Then switch the order on line 3 and watch the winner change.

Merge two settings objects
Step 0 of 4Ready
Your turn: follow the blue line

Switch the order on line 3 and step again. The same two objects give a different theme, because the object copied later wins.

Running in
  1. script
Next: line 1
const userSettings = { theme: "dark" };const merged = { ...defaults, ...userSettings };console.log(merged.theme + " / " + merged.pageSize);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose line 3: which object is copied first?

The object copied last wins, so this choice 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 one, name is always "X", because it comes last. In the second one, user.name comes last, so it replaces "X" if user has a name.

The next block tests some unusual cases. It uses two new tools:

  • Object.create(parent) makes an empty object that borrows properties from parent. Borrowed properties are not the object's own.
  • Object.defineProperty adds one property and lets you choose its settings, such as enumerable: false. A getter is a property whose value comes from a function. The function runs each time the property is read.
Unusual cases for object spreadPop 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 print {}, {"0":"J","1":"S"}, and {"label":"Ada"}:

  1. Line 1: object spread skips null and undefined, so safe is an empty object.
  2. Line 2: a string's characters are copied as keys 0 and 1.
  3. Line 9: the borrowed property and the hidden property are skipped. The getter runs, and its result, "Ada", is copied as a plain value.

The last line throws a TypeError. Spread in an array needs an iterable, and null is not iterable.

Object spread does not copy everything

Object spread copies own enumerable properties only. It skips borrowed properties and hidden (non-enumerable) properties. If a copied property is a getter, the getter runs, and the new object gets the plain value it returned.

The result is always a plain object. So the methods that an object borrows from its class are not copied. Use spread to copy data, not to copy a class object with its methods.

Rest in destructuring

STEP THROUGH

Destructuring takes values out of an array or object and puts them into variables. The left side, like [next, ...waiting], is called the pattern. Rest in a pattern gathers everything that the pattern did not already take. This is the opposite direction from spread.

Keep the first item apartPop out in the code editor (opens in a new tab)JavaScript
const queue = ["Asha", "Ben", "Chen"];
const [next, ...waiting] = queue;
console.log(next + " | " + waiting.join(", "));

Line 1 makes a queue of three names. Line 2 puts "Asha" into next. Then ...waiting gathers the names that are left into a new array. Line 3 prints Asha | Ben, Chen. Rest makes a new array; it does not remove anything from queue.

Rest in an array pattern, const [next, ...waiting] = queue. The queue holds "Asha", "Ben", and "Chen". "Asha" goes into a box called next. "Ben" and "Chen" are gathered into a new array in a box called waiting.const [next, ...waiting] = queue;queue"Asha""Ben""Chen""Asha"nextthe first item["Ben", "Chen"]waitingrest gathers the others
next takes the first item, "Asha". Then ...waiting gathers every item that is left into one new array. queue itself does not change.
Rest can gather nothingPop out in the code editor (opens in a new tab)JavaScript
const queue = ["tea"];
const [next, ...waiting] = queue;
console.log(next + " / " + waiting.length);

This queue has only one item, and it goes into next. No items are left over. Rest still makes an array, but the array is empty. So line 3 prints tea / 0. Your code can check waiting.length instead of guessing that a second item exists.

Step through rest in an object pattern and in an array pattern below.

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

Find the dots that gather leftovers. Then step through the object pattern and the array pattern.

Running in
  1. script
Next: line 1
const { password, ...safeUser } = user;const steps = ["draft", "review", "publish"];const [first, ...others] = steps;console.log(JSON.stringify(safeUser));console.log(first + " / " + others.join(", "));
CallStoreChangeResultBlue line: runs next. 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.

Both patterns are common in real code:

  • The object pattern { password, ...safeUser } leaves one property out and keeps all the others in safeUser.
  • The array pattern [first, ...others] keeps the first item apart and keeps the other items together in others.
Rest must come last

const [first, ...middle, last] = list; is a SyntaxError, an error JavaScript reports before the code runs. Rest means "gather everything that is left". After it, nothing is left, so JavaScript does not allow another variable after rest in the same pattern.

Merging & copying

Spread makes a new outer array or object. But the copy is shallow: it copies only the outer level. If a property holds another object, the copy points to that same inner object. You saw this idea in the Objects & references lesson: two variables can point to one object.

An inner object is still sharedPop out in the code editor (opens in a new tab)JavaScript
const profile = { name: "Asha", preferences: { theme: "dark" } };
const copy = { ...profile };
copy.preferences.theme = "light";
console.log(profile.preferences.theme);

Line 2 makes a new outer object, copy. But copy.preferences still points to the same inner object as profile.preferences. Line 3 changes that shared inner object. So line 4 prints light, even though the code never touched profile by name.

A shallow copy. Two carts: profile, the old object, and copy, the new object. Each holds a preferences card. Both preferences cards point to the same single inner object, theme "light". So a change made through copy also shows in profile.old objectpreferencesprofilenew copypreferencescopytheme: "light"one shared object
{ ...profile } makes a new outer object, copy. But its preferences property points to the same inner object as before. Change the theme through copy, and profile sees the change too.

To keep a change apart from the old object, copy the inner level too. An update that makes new objects instead of changing the old ones is called an immutable update.

Change a nested value without touching the old objectPop 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 rule: copy every object or array on the way to the value you change. Lines 6 to 8 do that:

  1. Line 6 copies the outer state object.
  2. Line 7 copies the inner user object, then sets a new name.
  3. Line 8 copies the tags array and adds one more tag.

Line 10 still prints Grace, line 11 prints Ada, and line 12 prints false because the two tags arrays are different arrays. Tools for screen updates, such as React and Redux, use this style. A new object is easy to spot as a change, so the tool knows what to redraw.

The table compares four ways to copy. Read each row: the tool, how deep it copies, when to use it, and what to watch for.

Four ways to copy
ToolHow deep does it copy?When is it a good fit?What should you watch for?
[...items]Only the outer level.Copying or rebuilding an array.Inner objects are still shared.
{ ...object }Only the outer level.Merging settings and updating state.A later property replaces an earlier one.
Object.assign({}, a, b)Only the outer level.Older code that merges objects.It also copies left to right, and it changes its first argument.
structuredClone(value)Every level, for the values it supports.Making a fully separate copy of data.It cannot copy functions, page elements, and a few other values.

Where you’ll use this

You use spread and rest whenever you want new data without changing the old data. These four patterns come up again and again:

  • Merge defaults with user settings: { ...defaults, ...userSettings } keeps every default the user did not set, and the user's choices win.
  • Leave out private fields: const { password, ...safeUser } = user; gives you an object without the password, ready to show or log.
  • Add to the end without changing the old array: [...items, newItem] returns a new array.
  • Insert or remove an item: spread the parts you want to keep, which you cut out with slice.
Check what you send

Object rest leaves out only the top-level fields you name. It does not look inside inner objects. If a private value is inside user.profile, { password, ...safeUser } does not remove it. When you send data to other people, pick the fields you want to share, or clean each inner object too.

Add a task without changing the old listPop out in the code editor (opens in a new tab)JavaScript
const todos = ["email", "shop"];
const nextTodos = [...todos, "walk"];
console.log(todos.join(" / ") + " -> " + nextTodos.join(" / "));

Line 2 makes a new list with one more task. Line 3 prints email / shop -> email / shop / walk. The old list is still there, so you can use it for an undo button or to compare old and new.

The next block inserts and removes an item at position index. items.slice(start, end) returns a new array with the items from start up to, but not including, end.

Insert and remove with spread and 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)];
  1. Lines 1 to 5: spread the items before index, then newItem, then the items from index on.
  2. Lines 6 to 9: spread the items before index, then the items after it. The item at index is skipped.

Order rules & pitfalls

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

  1. Spread in an array or a call keeps the items in their order.
  2. Object spread copies from left to right, and a later property wins.
  3. Rest in destructuring must come last, because it gathers everything that is left.
  4. Object spread copies own enumerable properties only.
  5. Spread visits every item it copies, so a bigger list takes longer to copy.
Very big lists

Copying with spread takes longer the more items there are. A function call can also take only so many arguments. If you spread a very large array into a call, JavaScript can throw a RangeError. For very large data, use a loop, reduce, or a function that takes the whole array as one argument.

Common misconceptions

“Spread and rest are the same thing.”

They use the same three dots, but they work in opposite directions. Spread lays items out; rest gathers them. The code around the dots tells you which one you have.

“Object spread makes a deep copy.”

It makes a new outer object only. Inner objects and arrays are still shared, unless you copy those levels too or use structuredClone.

“Object spread copies every property.”

It copies own enumerable properties only. It skips borrowed and hidden properties, and it runs getters to copy the values they return.

“Spreading null always throws.”

Object spread skips null and undefined. Spread in an array or a call throws with them, because it needs an iterable.

“Spread is free because it is short to write.”

The code is short, but the work is still a full copy. For everyday data that is fine. For very large lists, measure how long it takes and pick the tool on purpose.

Practice: spread & rest

5 EXERCISES

Guess the output first. Then run the code in your console or editor. Each answer is checked against what the code really prints.

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 · PracticeLeave out the password

    Object rest keeps the safe data and leaves the secret out. Which keys are left?

    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 · PracticeChange a nested value without touching the old object

      Guess the output. Notice that the old name stays the same.

      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 · PracticeGuess the 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 · ChallengeAdd an item without changing the old array

          Check 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 choice explains why it is right or wrong. For code questions, first look at the code around the dots: do they lay items out or gather leftovers?

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7Which sentence is the best way to remember the two jobs of ...?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this array spread code 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 this object merge code 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 this object rest code 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 this copy code 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 lays items out; rest gathers leftovers.
            • Spread in an array or a call lays out the items of an iterable, in order.
            • Object spread copies own enumerable properties, and a later property wins.
            • Rest in destructuring gathers what is left, and it must come last.
            • Spread copies only the outer level, and bigger copies take longer. Copy inner levels on purpose.

            Remember the one-liner.
            Spread lays values out; rest gathers the leftovers back in.

            Up next: Map & Set. You'll meet two collections: one that keeps each value only once, and one that can use any value as a key.

            CompleteFrontend Clear concepts. Working examples.