JavaScript uses the same three dots, …, for two different features: the spread operator and the rest parameter. They look identical, but they do opposite jobs. The trick to telling them apart is simple: it depends on where the dots show up.
🍞🥄 Spread Operator: Unpacking Things
Spread takes a collection and unpacks it into individual pieces.
const arr = [1, 2, 3];
console.log(...arr);
// same as console.log(1, 2, 3)
const obj = { a: 1, b: 2 };
const copy = { ...obj, c: 3 };
// { a: 1, b: 2, c: 3 }You use spread inside a function call, an array literal, or an object literal. It expands a single value into many.
🧺 Rest Parameter: Gathering Things
Rest does the opposite. It takes many separate values and gathers them into a single array.
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4);
// nums becomes [1, 2, 3, 4]You use rest only in a function’s parameter list. It collapses many arguments into one array.
The Rule of Thumb
Same symbol, opposite direction, and the position tells you which one you are looking at.

Debounce and Throttle
This pattern is easiest to see in a higher-order function, meaning a function that takes another function as an argument and returns a new function.
For example, I was looking back at some of the code examples I wrote, and I found the pattern when implementing throttle.
function throttle(fn, limit) {
let inThrottle;
return (...args) => { // rest: gather the incoming arguments into an array
if (!inThrottle) {
fn(...args); // spread: unpack that array back into arguments
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}Let’s break down the two roles in this code.
fnis the function you actually want to run. It is passed in as a parameter to the higher-order functionthrottle.argsrepresents whatever arguments the caller wants to pass intofnlater, whether that is zero arguments, one, or ten.
- The returned function uses rest to gather them all into
args. - When it is time to actually call
fn, it uses spread to unpackargsback into individual arguments, sofnreceives them exactly as if it had been called directly.
Rest collects, Spread releases, and the values pass through unchanged.
