A fun session. Some genuinely useful patterns and two JavaScript behaviours at the end that look like bugs but are actually the language working exactly as designed.
...rest vs ...spread — Same Syntax, Opposite Jobs
These both use ... but they do completely opposite things depending on which side of the assignment they're on. That's why they're easy to mix up.
...rest (Left Hand Side) — gathering things together
When you see ... on the left side of an assignment or in a function parameter, it's collecting whatever's left into an array:
function logAll(first, ...rest) {
console.log(first);
console.log(rest);
}
logAll(1, 2, 3, 4, 5); // first → 1 // rest → [2, 3, 4, 5]
Everything that didn't get its own name gets gathered into rest. That's why these are called variadic functions — they can accept any number of arguments.
...spread (Right Hand Side) — spreading things apart
When ... appears on the right side, it's doing the opposite — unpacking an existing array into individual elements:
let arr = [1, 2, 3, 4, 5]; let arr2 = [...arr, 6, 7, 8, 9, 10]; // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
You're not copying the array reference — you're spreading out its elements and building a brand new array. This is why spread is so useful for immutable updates in React.
Simple rule to remember: left side gathers, right side scatters.
reduce() — A for Loop With a Memory
reduce() is honestly just a cleaner abstraction of the classic accumulator pattern. Here's the same operation written both ways so you can see exactly what it replaces:
With a for loop:
let arr = [1, 2, 3, 4, 5]; let sum = 0; for (let i = 0; i < arr.length; i++) { sum += arr[i]; } console.log(sum);
With reduce():
let arr = [1, 2, 3, 4, 5]; let sum = (acc, elem) => acc + elem; let result = arr.reduce(sum, 0); console.log(result);
Same result. reduce() just handles the loop and the accumulator internally so you only have to think about the logic.
A few things worth locking in about reduce():
reduce() takes two arguments — a callback function and the initial value of the accumulator. In the example above that's sum and 0.
The initial value (0 here) only kicks in for the very first iteration. After that, the accumulator carries whatever value the previous iteration returned.
And importantly — respect lexical scoping. Call reduce() after your callback function is defined, not before. JavaScript needs to know what sum is before you reference it.
JavaScript Trick #1 — The Accidental Index Selector
This one looks like an array with two arrays inside it but it's actually something completely different:
let arr = [1, 2, 3, 4, 5][0, 3]; console.log(arr); // Output: 4
What's happening here — the second [0, 3] is not a second array. It's bracket notation accessing an index on the first array. And inside it, 0, 3 is the comma operator — which evaluates both expressions left to right and returns the last one. So [0, 3] evaluates to 3, and arr[3] is 4.
So the whole thing reads as: "give me index 3 of this array" — which is 4. Looks like two arrays, behaves like an index accessor. Wild.
JavaScript Trick #2 — Duplicate Keys in Objects
let obj = { a: 1, b: 2, a: 3 }; console.log(obj); // Output: { a: 3, b: 2 }
Objects don't support duplicate keys — just like Sets don't support duplicate values. When JavaScript sees two properties with the same key, the second one overwrites the first. But here's the subtle part — it doesn't just replace the value and move on. It goes back to the first appearance of that key and updates its value there. That's why a stays in its original position in the output but holds the newer value 3.
So the object ends up as { a: 3, b: 2 } — not { b: 2, a: 3 }. Position is preserved, value is overwritten.
Top comments (0)