Smooth Operators


JavaScript has come a long way since its humble beginnings. If you've been riding the ES6 wave with arrow functions and promises, it's time to level up your code with the refined features from ES9 (2018) and ES10 (2019). These updates might not be as flashy as their predecessors, but they solve real-world pain points that developers face daily.
The Object Spread Revolution
Remember the days of awkwardly merging objects with Object.assign()
? ES9 brought us the elegant object spread operator, making object manipulation feel as natural as breathing.
The object spread operator (...
) lets you create shallow copies, merge objects, and override properties with beautiful syntax that feels intuitive. It's like Object.assign()
got a makeover and now looks stunning.
One of the most loved additions to ES9 is the introduction of rest and spread properties for objects. It's like they took the beauty of rest/spread for arrays (hello, [...array]
) and brought it to objects.
For example, when you want to clone an object:
const obj = { name: 'Alice', age: 30 };
const cloned = { ...obj };
console.log(cloned); // { name: 'Alice', age: 30 }
But the real magic happens when you need to merge objects:
const obj1 = { name: 'Alice', age: 30 };
const obj2 = { job: 'Developer', location: 'NYC' };
const merged = { ...obj1, ...obj2 };
console.log(merged);
// { name: 'Alice', age: 30, job: 'Developer', location: 'NYC' }
Promise.finally(): The Cleanup Crew Has Arrived
Promises are great, but there was always something missing: a way to run cleanup code regardless of success or failure. ES9 completed the Promise API with .finally()
.
We've all been there: making promises, handling .then()
, .catch()
, and wondering how to run some code after the promise settles, regardless of whether it succeeds or fails. That's where finally()
comes in.
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('Data loaded'), 2000);
});
};
fetchData()
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
console.log('Cleanup code runs here');
});
This eliminates the need to duplicate cleanup code in both .then()
and .catch()
blocks. Whether you're resetting loading spinners, closing connections, or cleaning up resources, .finally()
has your back.
Turning Your Arrays Flat as a Pancake
Nested arrays are the bane of many developers' existence. ES10 gives us elegant solutions with flat()
and flatMap()
.
Nested arrays? Flatten them! ES10 introduces the flat()
method, which lets you flatten an array of arrays into a single-level array with one simple call. If you're dealing with more complex arrays or want to perform some transformations at the same time, flatMap()
is the superhero you didn't know you needed.
const nestedArray = [1, [2, 3], [4, [5, 6]]];
const flattened = nestedArray.flat(2); // Flatten to a depth of 2
console.log(flattened); // [1, 2, 3, 4, 5, 6]
The beauty of flat()
is that you can control the depth of flattening. Need to go deeper? Just pass a larger number.
But wait, there's more! flatMap()
combines mapping and flattening in one go:
const arr = [1, 2, 3];
const result = arr.flatMap(x => [x, x * 2]);
console.log(result); // [1, 2, 2, 4, 3, 6]
This is perfect for scenarios where each item in your array might produce multiple results.
Object.fromEntries(): The Missing Puzzle Piece
ES10 introduced a perfect counterpart to Object.entries()
with Object.fromEntries()
. Now you can convert arrays back to objects with ease.
There are plenty of scenarios where you might want to convert an array of key-value pairs into an object. With ES10, Object.fromEntries()
is here to make that process a breeze.
const entries = [['name', 'Alice'], ['age', 30]];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'Alice', age: 30 }
This feature completes the data transformation cycle. You can now easily:
Convert objects to entries with
Object.entries()
Transform those entries with array methods like
map()
orfilter()
Convert back to an object with
Object.fromEntries()
Real-World Application: Data Transformation Pipeline
Let's put it all together with a practical example. Imagine you have a collection of user data and need to transform it:
Here's what you can do:
Extract specific properties with object rest/spread
Transform the data with array methods
Flatten nested results with
flatMap()
Convert arrays back to objects with
Object.fromEntries()
The result? A clean, readable data pipeline that would have been much messier in pre-ES9/10 days.
String Trimming, Refined
ES10 also standardized string trimming with trimStart()
and trimEnd()
, making the API more consistent with other directional methods like padStart()
and padEnd()
.
These methods provide clearer naming than the older trimLeft()
and trimRight()
(which still work but aren't as semantically precise).
Optional Catch Binding: Less Noise, More Signal
Finally, ES10 introduced optional catch binding, letting you omit the error parameter when you don't need it:
Before:
try {
// Risky code here
} catch (error) {
// Unused error parameter
console.log('An error occurred');
}
After:
try {
// Risky code here
} catch {
// No unused parameter!
console.log('An error occurred');
}
Why These Features Matter
These ES9 and ES10 features might seem small individually, but collectively they represent a significant enhancement to JavaScript's expressiveness. They allow us to:
Write more declarative code that expresses what we want, not how to get it
Reduce boilerplate and noise
Build more elegant data transformation pipelines
Handle common edge cases more gracefully
The JavaScript community has embraced these features because they address real pain points in everyday coding. Modern libraries and frameworks increasingly leverage these capabilities to provide cleaner APIs.
Browser Support and Adoption
Good news! Most modern browsers now support these features. Node.js has also incorporated them in recent versions. If you need to support older environments, transpilers like Babel have you covered.
What's Next?
JavaScript's evolution continues to bring us powerful features that make our code more robust and readable. Let's dive into some of the most impactful additions from ES11 (ECMAScript 2020) that are transforming how we write JavaScript in 2024.
ES11 brings even more goodies with optional chaining (?.
) and nullish coalescing (??
), which we'll explore in our next article. These features further refine JavaScript's handling of potential nulls and undefined values.
Conclusion
ES9 and ES10 introduced features that have made JavaScript cleaner, smarter, and more efficient. Whether it's iterating over arrays and objects with ease, flattening deeply nested structures, or converting arrays to objects, these updates help you write less boilerplate and focus on the logic that matters.
The beauty of JavaScript's evolution is that it builds upon itself. Each new version brings features that work harmoniously with previous ones, creating a more cohesive language. These aren't just new toys to play with—they're practical solutions to problems developers face every day.
So go ahead, spread those objects, flatten those arrays, and transform your data with newfound elegance. Your future self (and your code reviewers) will thank you.
Subscribe to my newsletter
Read articles from Mikey Nichols directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Mikey Nichols
Mikey Nichols
I am an aspiring web developer on a mission to kick down the door into tech. Join me as I take the essential steps toward this goal and hopefully inspire others to do the same!