Comma operator
Today's subject is the comma operator. It's been there since forever (which in JavaScript is year 1997), and it seems to be weirdly inspired by the Lisp's do
construct. It evaluates every statement in the chain and returns the last one. It's rarely used in the source code, but I saw it a couple of times in the transpiled code, which I read sometimes for my own pleasure (or debugging issues with missing source maps, but that's pleasure too).
With return
It can be used after the return
keyword. With an arrow function, we'll have to wrap it in parentheses, which additionally evaluates any statement. That is something that I saw in transpiled bits often.
// the original take
function counterFactory() {
let count = 0;
return function counter() {
count = count + 1;
return count;
}
}
// and yes, 'return ++count' would do here, but it's an abstract example ;)
// the classic function
function counterFactory_commal() {
let count = 0;
return function counter() {
return count = count + 1, count;
// ^^^^^^^^^^^^^^^^^ the bump happens as a part of return
}
}
// the arrow function -- note how we have to use additional parentheses
// because the engine would just return 'count' instead of the function
function counterFactory_comma2() {
let count = 0;
return () => (count = count + 1, count);
// ^^^^^^^^^^^^^^^^^ or here
}
// then
var counter = counterFactory_comma2();
counter(); // 1
counter(); // 2
counter(); // 3, etc.
With Array's push
and the arrow-function's implicit return
The other example was used by me in a discussion on former Twitter about the impossibility of inlining Array's push
in the implicit return of an arrow function. It would allow not creating a new array in every loop, but the function is not chainable as it returns the length of the updated array. However, if we wrap it in parentheses and push through the comma operator, it will work. The example was vehemently objected in the discussion. And rightly so.
// the original take: with a new array created in each iteration
function positiveSquares(array) {
return array.reduce((acc, number) => {
if (number > 0) {
return [...array, number ** 2];
}
return array;
}, []);
}
// the optimisation: with `array` reused
function positiveSquares_withPush(array) {
return array.reduce((acc, number) => {
if (number > 0) {
acc.push(number 0* 2);
}
return acc;
}, []);
}
// the optimisation with comma operator, or: how to impress your fellow devs
function positiveSquares_withPushAndComma(array) {
return array.reduce(
(acc, number) =>
number > 0
? (acc.push(number ** 2), acc)
// ^^^^^^^^^^^^^^^^^^^^^ we push to array
// ^^^ and we return it
: acc,
[],
);
}
Comma operator itself is very implicit and its readability is quite low, so it is advised to not use it professionally, as it might be confusing to other developers. To the credit of every single person that I worked with in the last two decades, no one has ever tried to use that. But is there any even remotely acceptable use case for it?
Inline console.logging
Well, there is. Every now and then, when we console-log-debugging something, it happens that there is no room for injecting console.log
without rewriting a piece of code. Like in the lambda below: on the first sight, it would require expanding into an explicit return, but it's enough to first wrap it in parentheses and then add console.log
. Quick and not so intrusive. And due to strong reactions to comma operator itself, no code review will let that fly into production (if console.log
itself is not enough).
// the original take
function absolutes(array) {
return array.map(number => Math.abs(number));
}
// with an added console. log but also a rewrite extensively
function absolutes_withElaborateLog(array) {
return array.map(number => {
console.log(number);
return Math.abs(number);
});
}
// with kind of injected console. log
function absolutes_withStealthLog(array) {
return array.map(number => (console.log(number), Math.abs(number)));
// ^^^^^^^^^^^^^^^^^^^ inject console.log
}