Modern Operators: Rest, Optional Chaining, Nullish & Map/Set
A handful of small modern operators that make everyday code safer and shorter — and two new collections beyond arrays and objects.
What you will learn
- Collect leftovers with the rest pattern
- Read deep data safely with ?. and ??
- Use Map and Set for lookups and de-duplicating
Rest — collect the leftovers
You saw the spread ... earlier (it spreads items out). The same three dots used on the left side does the opposite: it gathers several values into one array or object. That gathering use is called the rest pattern.
A common use is a function that accepts any number of arguments — you collect them all into one array.
<script>
// ...numbers gathers every argument into an array called numbers
function sumAll(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
document.write(sumAll(2, 4, 6) + "<br>"); // 12
document.write(sumAll(10, 20, 30, 40)); // 100
</script>Inside sumAll, ...numbers scoops up every argument you pass into a real array. So sumAll(2, 4, 6) makes numbers equal to [2, 4, 6], and reduce adds them to 12. The next call passes four numbers and they are all gathered the same way — the function adapts to however many you give it.
Note: Output: 12 100
Rest also works when destructuring — grab the first item, gather the rest into an array:
<script>
const [winner, ...others] = ["Asha", "Ravi", "Maya", "Sam"];
document.write("Winner: " + winner + "<br>");
document.write("Runners-up: " + others.join(", "));
</script>winner takes the first item ("Asha") by position; then ...others sweeps up everything left into a new array ["Ravi", "Maya", "Sam"]. So one line splits a list into "the first" and "all the rest".
Note: Output: Winner: Asha Runners-up: Ravi, Maya, Sam
Optional chaining ?. — read deep data without crashing
Real data often has missing pieces. If you read user.address.city but address does not exist, JavaScript crashes with an error. The optional chaining operator ?. says "only keep going if this part exists; otherwise just give back undefined quietly".
<script>
const user1 = { name: "Asha", address: { city: "Bengaluru" } };
const user2 = { name: "Ravi" }; // no address at all
document.write(user1.address?.city + "<br>"); // Bengaluru
document.write(user2.address?.city); // undefined (no crash)
</script>For user1, address exists, so address?.city reads "Bengaluru" normally. For user2, there is no address — without ?. the line user2.address.city would throw "Cannot read properties of undefined". The ?. stops safely at the missing part and returns undefined instead of crashing the whole script.
Note: Output: Bengaluru undefined
Nullish coalescing ?? — a smarter default
The ?? operator supplies a fallback value, but only when the left side is null or undefined (the two "truly missing" values). This fixes a long-standing bug with the older || operator, which also treats 0 and "" (empty string) as "missing" — even though they are real, valid values.
<script>
const stock = 0; // a real value: zero items in stock
document.write("With ||: " + (stock || "unknown") + "<br>"); // "unknown" — WRONG
document.write("With ??: " + (stock ?? "unknown")); // 0 — correct
</script>Here stock is 0 — a perfectly valid count. stock || "unknown" wrongly replaces it with "unknown", because || treats 0 as "empty". But stock ?? "unknown" keeps the 0, because 0 is not null or undefined. Use ?? for defaults whenever 0 or "" could be legitimate values.
Note: Output: With ||: unknown With ??: 0
Watch out: Common bug: using || for default values when 0, "" or false are legitimate. Reach for ?? when you only want to replace genuinely missing (null/undefined) values.
Set — a list with no duplicates
A Set is a collection like an array, but it automatically refuses duplicates. The most common use is removing repeats from a list in one line.
<script>
const tags = ["js", "css", "js", "html", "css", "js"];
const unique = [...new Set(tags)]; // de-duplicate
document.write(unique.join(", "));
</script>new Set(tags) builds a Set from the array, and because a Set ignores repeats, only one of each value survives. The spread [...new Set(tags)] pours those unique values back into a normal array, giving the de-duplicated list — a tidy one-line trick used everywhere.
Note: Output: js, css, html
Map — keyed lookups (any key type)
A Map is like an object: it stores key → value pairs. The differences are that a Map keeps its items in insertion order, lets keys be any type (not just strings), and has handy methods like .set, .get and .size.
<script>
const scores = new Map();
scores.set("Asha", 90);
scores.set("Ravi", 75);
document.write("Asha: " + scores.get("Asha") + "<br>");
document.write("Players: " + scores.size);
</script>new Map() creates an empty Map. .set("Asha", 90) stores a pair, and .get("Asha") reads the value back (90). .size counts the pairs — like .length for arrays. Maps shine when you build up keyed data and want easy reading and counting.
Note: Output: Asha: 90 Players: 2
Tip: Rule of thumb: use a plain object for fixed, known fields (a user’s name/age); use a Map when you keep adding/looking up pairs by key; use a Set when you just need unique values.
Q. Why prefer ?? over || for a default value?
✍️ Practice
- Write a function with a rest parameter that returns the largest of any number of arguments.
- Use
?.to safely read a nested property that may be missing, and??to give it a default. - De-duplicate an array of repeated values using a
Set.
🏠 Homework
- Take a small dataset (array of user objects) and use optional chaining + nullish coalescing to print a field that some users are missing, with a sensible default.