Spread is unpacking your lunchbox onto the table, so everything comes out. Rest is the opposite. You keep the apple and sweep whatever's left into one bag. Both save you from copying things one by one, or from needing to know ahead of time how many values you'll get.
Spread: unpack an array
Spread unpacks an array right where you put it. Write ...nums inside a new array, and the items of nums drop into place.
const nums = [1, 2];
const more = [...nums, 3];
console.log(more); // prints: [ 1, 2, 3 ]
console.log(nums); // prints: [ 1, 2 ]
The old array doesn't change. The same trick copies an array with [...nums] or joins two with [...a, ...b]. Keep in mind the copy is shallow, so objects inside are shared between both arrays.
Spread into a function call
Spread also works for passing values into a function. Math.max finds the biggest number, but it wants separate numbers. Hand it an array and it can't read it.
const nums = [1, 2];
console.log(Math.max(nums)); // prints: NaN
console.log(Math.max(...nums)); // prints: 2
With the dots, the array is unpacked into separate values, as if you had typed Math.max(1, 2).
Spread works on more than arrays. Spreading a string gives you its characters, and spreading an object inside new curly braces copies its properties. Anything you write after the spread wins, which makes it a neat way to copy an object and change one thing.
console.log([..."hi"]); // prints: [ 'h', 'i' ]
const cat = { name: "Mia", age: 2 };
console.log({ ...cat, age: 3 }); // prints: { name: 'Mia', age: 3 }Rest: gather values into one array
Rest is spread in reverse. In a function's parameters, the dots collect the values into one array.
function sum(...nums) {
return nums.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3)); // prints: 6
console.log(sum(5, 10)); // prints: 15
Call sum(1, 2, 3) and nums becomes [1, 2, 3]. Nothing is named before it, so it gathers all of them. Now your function can take as many values as you want.
If you name some parameters first, rest only gets the leftovers. That's the keep-the-apple version.
function pack(first, ...others) {
return first + " | " + others.join(", ");
}
console.log(pack("apple", "sandwich", "cookie")); // prints: apple | sandwich, cookieThe twist: telling them apart
Same dots, different place. When the dots sit where values get collected, like in a function's parameters, that's rest. Anywhere else, like in a new array or a function call, they unpack, and that's spread.
function f(...x)collects, so it's rest.[...x]unpacks, so it's spread.f(...x)unpacks, so it's spread.
You'll also see rest on the left side of = when pulling values out of an array, like const [first, ...others] = lunch. It collects there too, so it's rest.
A quick way to check is to ask which way things are moving. If many separate values are being packed into one array, it's rest. If one array (or object) is being opened up into separate pieces, it's spread. Reading code out loud helps too. Say "the rest of the values" for rest and "spread out" for spread, and see which one fits the sentence.
Rule of thumb
Spread unpacks things, and rest gathers them up.
Putting a rest parameter anywhere but last, like function pack(...others, last). That's a SyntaxError, because rest takes everything that's left. Use only one, and keep it at the end.
Check yourself
Dots in a function's parameters collect values, so that's rest. nums becomes an array of everything passed in.