Same Syntax, Two Jobs
The three dots ... show up in a lot of modern JavaScript code, and they do two opposite things depending on where they appear. Once you see the pattern, every use of ... falls into one of two buckets:
- Rest:
...nameon the receiving side collects multiple values into one array or object. - Spread:
...valueon the giving side expands an array or object into its individual pieces.
That's the whole mental model. The rest of this page is just examples of each, and the patterns you'll keep reaching for.
Rest Parameters: Collecting Arguments
A rest parameter in a function definition gathers any number of arguments into a real array:
nums is an ordinary array. You can .map it, .filter it, check its .length, pass it to another function — everything arrays do.
Rest parameters can mix with regular parameters, but the rest parameter has to come last:
label swallows the first argument; everything after it lands in items. Writing the rest parameter anywhere but last is a syntax error.
Rest vs. the Old arguments Object
Older JavaScript code uses a magic variable called arguments inside regular functions. It looks like an array but isn't one, which means array methods don't work on it directly. Rest parameters replace it cleanly:
Arrow functions don't even have an arguments object, so rest parameters are the only way to accept a variable number of arguments in them. Prefer ...args in any new code.
Spread in Function Calls
Spread does the reverse: it takes an array and unpacks it into individual arguments at the call site.
Math.max takes individual numbers, not an array. Before spread, you'd write Math.max.apply(null, nums). Now it's a ... and you're done.
Notice how the exact same ... is rest in the function definition and spread in the function call — the position tells you which one it is.
Spread in Array Literals
Spreading into an array literal copies or combines arrays:
[...a] gives you a fresh array with the same elements — useful when you want to sort or mutate without touching the original:
scores is untouched because .sort ran on the copy. Small habit, big payoff when you're writing code that shouldn't have surprising side effects.
Spread in Object Literals
Spread works on plain objects too, merging their properties into a new one:
Later keys win. updates.age overrides user.age, and city comes along for the ride. The order of the spreads determines the result — keep that in mind when you're stacking defaults and overrides:
Defaults first, user choices second. The user wins on fontSize, inherits theme.
The Shallow Copy Trap
Spread copies one level deep. Nested objects and arrays are still shared between the original and the copy:
Both arrays show the new tag because copy.tags and original.tags are the same array. Spread didn't clone the nested list — it just copied the reference.
For a true deep copy of plain data, reach for structuredClone:
Now the two arrays are independent. structuredClone is built into modern browsers and Node, handles nested structures, and is the right call whenever "shallow" won't cut it.
Rest in Destructuring
Rest works in destructuring too, where it collects the leftover elements or properties:
Pulling a few fields out and keeping the rest in a single object is a common pattern when forwarding props, stripping sensitive fields, or building patched versions of data:
password gets extracted (and ignored); safe holds everything else. No mutation, no manual copying.
A Quick Recap
...namein a parameter list or destructuring pattern is rest: it collects....valuein a function call, array literal, or object literal is spread: it expands.- Spread copies are shallow. Nested structures stay shared. Use
structuredClonefor deep copies. - Rest parameters are real arrays — use them instead of
arguments. - Later spreads override earlier ones in object literals, which is how you build defaults-plus-overrides.
Next: Closures
Functions in JavaScript don't just take inputs and return outputs — they also remember the scope they were defined in. That memory is called a closure, and it's the mechanism behind callbacks, factories, and most of the patterns you'll meet on the next page.
Frequently Asked Questions
What's the difference between rest and spread in JavaScript?
They use the same ... syntax but do opposite jobs. Rest collects multiple values into a single array — it shows up in function parameter lists and destructuring. Spread expands an iterable or object into its pieces — it shows up in function calls, array literals, and object literals. If ... is on the receiving side, it's rest; if it's on the giving side, it's spread.
How do rest parameters work in a function?
A rest parameter like function sum(...nums) collects every argument passed to the function into a real array named nums. It must be the last parameter in the list. Unlike the older arguments object, a rest parameter is a true array, so .map, .filter, and .reduce work directly on it.
Does the spread operator make a deep copy?
No. Spread copies one level only — a shallow copy. { ...user } gives you a new object with the same top-level keys, but nested objects and arrays inside are still shared references. For a deep copy, use structuredClone(value) or serialize through JSON for plain data.