JavaScript is sometimes quite verbose, isn’t it? If conciseness and effectiveness matter to you, a nice programming motto might be: write less, do more. (jQuery, I’m looking at you!)
Writing complex and powerful code on a single line is often an exciting experience where the only tangible limit is readability. One-liners in JavaScript represent a key topic that has been boosted by the arrival of ECMAScript 2015 (ES6). We cover this right here.
Ancestral techniques (ES5)
ECMAScript 5 is definitely not the best specification to show off with one-liners. In fact, one-liners are rarely used in ES5 because they tend to be too long due to syntax limitations. ES5 one-liners are generally allowed by simple methods in Array.prototype
and/or String.prototype
(e.g. concat
, reverse
, slice
, join
, replace
, etc.).
Here is a “WTF example” to illustrate what we are talking about:
|
Nevertheless, we can go further with ES5 thanks to some well-known JS techniques and idioms that make it possible to write more concise code…
Currying
Currying is generally used to reduce functions arity (number of arguments), but depending on your code structure, it may be used as a hack to get a “light” invocation pattern.
This is the classical way of doing things:
|
Calling each function on a single line gives us the following code:
|
Not so cool, actually. With currying, we could transform our code and make it look like this:
|
In this particular situation, invoking each function would be more “one-liner compliant”:
|
That’s it! foo()()()
is much more concise because…
foo()
invokesfoo
andfoo
returnsbar
foo()()
invokesbar
andbar
returnsbaz
foo()()()
invokesbaz
andbaz
returns a string.
Currying is nice, but chances are you will prefer to use the classical approach which is far more readable in most cases…
Method chaining
Method chaining is a very popular pattern in JavaScript which is frequently implemented in JS libraries. In fact, it is an interesting pattern to flatten method invocation and enable developers to chain long operations (possibly on a single line).
Let’s take a very basic and naive example: a counter “class” that has a single attribute (val
) and a single prototype method (add
):
|
If we want a counter to be incremented three times, we will end up with the following line:
|
This code works, but writing it on a single line looks terrible. With method chaining, this is obviously much prettier:
|
To get this amazing behavior, Counter.prototype.add
just have to return this
!
Immediately-Invoked Function Expressions (IIFEs)
Regular functions must be explicitly invoked before (thanks to hoisting) or after they have been declared to perform some action:
Before
|
After
|
Creating a one-liner with such a function is trivial:
|
Well… It is not fantastic, is it?
Using an IIFE, that is to say a function expression that calls itself, would be more convenient:
|
The ternary operator
This operator can be used as a shortcut for if
/else
statements. For instance, this code is a bit too verbose to be refactored on a single line:
|
If we try, we can see that the result is painful to read:
|
With the ternary operator, we do not have this problem:
|
The ternary operator is, to some extent, a powerful factory of one-liners. But, be careful. Any abuse make the code unreadable:
|
Short-circuit operators
Just like the ternary operator, these operators (&&
and ||
) are mostly used in a boolean context and are perfect to replace if
/else
statements that would be hard to read in a one-liner.
|
But since any value can be truthy or falsy in JavaScript, expressions using short-circuit operators may also return non-boolean values…
|
&&
:
- When the left operand is truthy, the next operand is evaluated
- When the left operand is falsy, the next operand is not evaluated and the current value is returned
||
:
- When the left operand is truthy, the next operand is not evaluated and the current value is returned
- When the left operand is falsy, the next operand is evaluated
Modern techniques (ES6)
ES6 has been designed with conciseness in mind and it brought a couple of useful features that tend to encourage one-liners:
- Arrow functions
- The spread operator
- Destructuring with enhanced object literals
Map-Filter-Reduce
For this example, we have a simple array: [1, 2, 3]
. We want to:
- Multiply by 2 each number in this array (
map
) - Keep the resulting numbers that are lower than 5 (
filter
) - Add up the remaining numbers (
reduce
)
With ES5, a one-liner would be utterly awful:
|
Thanks to ES6 arrow functions, it becomes much more stylish:
|
Iterable protocol
ES6 allows us to perform complex transformations on iterable data structures with ease. Let’s do something totally useless in practice, but relevant for learning purposes:
|
In this example, we did something really complex. Without the spread operator, it would have been much more verbose. Let’s try to replace it with for...of
:
|
The thing is that… This verbose version is probably more readable than the one-liner. But if we take a simpler example, the spread operator looks fabulous:
|
Data extraction
Imagine that you have the following data structure:
|
With ES5, if you want to extract everything on a single line, it will most likely be a nightmare:
|
But thanks to destructuring with enhanced object literals, it can be achieved with comfort:
|
In both cases, console.log(foo, bar, baz, qux, quux)
will give Foo Bar Baz Qux Quux
.
Practical examples
Finding the longest word in a given string
|
Capitalizing the first letter of each line (for multiline strings)
|
Removing duplicates from an array
|
Checking if all elements of an array are in another one
|
Swaping values of object properties
|