Code evolves over time. JavaScript in particular has evolved a lot in the last couple years. It can be hard to keep up with all the new features in the language! It helps to see concrete examples. In this blog post, I will walk through a hypothetical (but realistic) example of how an ordinary ES3 JavaScript function, subjected to the harsh environment of “changing requirements,” can utilize ES2015+ JavaScript features to become the ultimate version of itself!

This was good enough for Imaginary Company’s website in the early 2000s. But now Imaginary Company has gone global, and the boss has asked us to update the greet function. She wants greet to automatically be translated into the correct language for the user. We are resourceful, but also lazy, so we decide to use Google Translate.

Here is the first big change: the Google Translate function returns a Promise. Our boss already told us to stop using callbacks, so that means our greet function must also return a Promise. Luckily, working with Promises is not as difficult these days thanks to async functions which were added to JavaScript officially in ES2017.

Asynchronous calls are contagious — once you introduce an asynchronous function into your code, everything that uses that function becomes asynchronous! So I suggest you embrace that early on in your design. Even if your function is actually synchronous, you can tack async in front of it and now you’ve future-proofed it. If you ever need to change the implementation and it becomes asynchronous, no big deal because all your existing code already is calling it with await.

Now the boss comes to us with another request. Sometimes the user’s native language isn’t known. She tells us the function needs to default to English if the language parameter is undefined.

It used to be you needed code like this in your function to implement default values:

arg = arg || 'foo'

But that had the unfortunate edge case of not working if arg was 0 or false. So to be safe, we were told to do fancier checks like:

arg = typeof arg!=="undefined" && arg!==null ? arg : 'foo'

which of course most people never did. But it is easy now! Setting the default values in the function declaration is nice for readability and IDEs will show that information as well.

Just as we’re feeling clever, the boss adds a new requirement. Some languages use a different name order convention. First name and last name are out, she says. Now it is “given name” and “family name” and we’ll determine which is first and which is last based on a flag.

Our development team is spread over the world, and some developers are used to listing family name before given name, and some given name before family name. Thus they have trouble remembering whether true means family name first or given name first.

Our boss is displeased. For any given instance of the greet function, she can’t tell without consulting the documentation whether the arguments are correct. We are given a new challenge: make the greet function fool-proof.

Some languages like Python have named arguments. Named arguments are super-cool because you don’t have to remember what order the arguments are in, and it’s obvious to whoever has to read the code what the arguments are (what some might brazenly call “self-documenting code”). Using JavaScript’s object destructuring, we can achieve something very similar to named arguments.

async function greet ({given, family, lang='en', reverse=false}) {

All we did is add a pair of curly braces but suddenly the nature of the function has changed. It now takes a single argument, and that argument is an object:

The boss loves this! It is easier to read, and no one is mixing up given name and family name anymore.

__

Highfive!

At first, the other developers complain that this new syntax is verbose. But then they discover a neat trick: if they name their variables the same as their properties, they can use the shorthand literal syntax:

They feel quite clever. The boss loves that everyone is starting to use the same variable names for data throughout the app; the desire to keep the code “DRY” has finally motivated the other developers to update old code that still used variables called firstname and lastname or firstName and lastName to use given and family.

But now there’s the potential new kind of error. If either lang or reversed are misspelled, then greet won’t see those properties and will silently use the default value instead. You realize you’ve traded worrying about what order arguments are in to worrying about how the arguments are spelled.

Our final function takes advantage of many modern JavaScript “super powers”:

async functions

default parameters

object destructuring

rest properties

I’ve started to write all my functions using these features because they provide a flexible foundation for the function to adapt to whatever new requirements come its way. What does your ultimate function look like?