94

I read this question about the "comma operator" in expressions (,) and the MDN docs about it, but I can't think of a scenario where it is useful.

So, when is the comma operator useful?

Community
  • 1
  • 1
gdoron is supporting Monica
  • 136,782
  • 49
  • 273
  • 342
  • 2
    `var i, j, k;` vs `var i; var j, var k`? – Salman A Mar 06 '12 at 07:30
  • 15
    @SalmanA. I'm not sure it has anything to do with the `,` operator. That line is valid in `C#` as well, but the `,` operator doesn't exist there. – gdoron is supporting Monica Mar 06 '12 at 07:46
  • The `,` operator doesn't exist in C#? You need to do some research. – Salman A Mar 06 '12 at 11:46
  • 2
    @SalmanA. I did. Didn't find it, enlight me... – gdoron is supporting Monica Mar 06 '12 at 12:08
  • 6
    @SalmanA a `,` is not always the `,` operator (and it never is the `,` operator in C#). So C# can lack a `,` operator while still freely using `,` as part of the syntax. – Seth Carnegie Mar 06 '12 at 12:24
  • You're both right. I've posted a new answer specific to JavaScript. – Salman A Mar 06 '12 at 12:26
  • 9
    I think the answers here have summed up the fact that the `,` isn't widely used *(and not every occurrence of a `,` is the comma operator)*. But you can borrow it and an Array to do a variable swap inline without creating a temporary variable. Given that you want to swap the values of `a` and `b`, you can do `a = [b][b = a,0]`. This places the current `b` in the Array. The second `[]` is the property access notation. The index accessed is `0`, but not before assigning `a` to `b`, which is now safe since `b` is retained in the Array. the `,` lets us do the multiple expressions in the `[]`. –  Mar 06 '12 at 15:30
  • 1
    ...though there are ways to accomplish this without the `,` like... `a = {b:b, a:b=a}['b']` or like this... `a = [b, b=a][0]` –  Mar 06 '12 at 15:32
  • @amnotiam. comments again... Can you please add a Fiddle? – gdoron is supporting Monica Mar 06 '12 at 15:35
  • 1
    Yeah, I really don't have much to add to the existing answers. [Here's a fiddle](http://jsfiddle.net/Yr7hJ/) showing all 3 swaps. The first one uses the `,` but again it can be done without as shown by the other two. –  Mar 06 '12 at 15:42
  • ...one thing the `,` operator version does is avoid the unused assignment to the Object or Array. This is a micro optimization, but may be important to someone. –  Mar 06 '12 at 15:51

14 Answers14

136

The following is probably not very useful as you don't write it yourself, but a minifier can shrink code using the comma operator. For example:

if(x){foo();return bar()}else{return 1}

would become:

return x?(foo(),bar()):1

The ? : operator can be used now, since the comma operator (to a certain extent) allows for two statements to be written as one statement.

This is useful in that it allows for some neat compression (39 -> 24 bytes here).


I'd like to stress the fact that the comma in var a, b is not the comma operator because it doesn't exist within an expression. The comma has a special meaning in var statements. a, b in an expression would be referring to the two variables and evaluate to b, which is not the case for var a, b.

Ivan
  • 11,733
  • 5
  • 35
  • 63
pimvdb
  • 141,012
  • 68
  • 291
  • 345
  • 3
    How did you think about that? Did you read it some where? Does it really being used? – gdoron is supporting Monica Mar 25 '12 at 22:21
  • 17
    I was just messing around with [Closure Compiler](http://closure-compiler.appspot.com/home) the other day to see what it actually does, and I did notice this substitution. – pimvdb Mar 26 '12 at 15:45
  • 3
    A similar use which I think is useful in your code would be for assigning multiple variables in an inline if statement. For example: `if (condition) var1 = val1, var2 = val2;` I personally think avoiding brackets where possible makes code more readable. – Aidiakapi Jan 03 '14 at 21:04
  • 2
    About the only times I use the comma operator are when I'm adding log statements to expressions (foo => (console.log('foo', foo), foo)), or if I'm getting too clever with reduce iteratees. (pairs.reduce((acc, [k, v]) => (acc[k] = v, acc), {})) – Joseph Sikorski Jul 10 '18 at 18:48
41

The Comma Operator is frequently useful when writing functional code in Javascript.

Consider this code I wrote for a SPA a while back which had something like the following

const actions = _.chain(options)
                 .pairs() // 1
                 .filter(selectActions) // 2
                 .map(createActionPromise) // 3
                 .reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4
                 .value();

This was a fairly complex, but real-world scenario. Bear with me while I explain what is happening, and in the process make the case for the Comma Operator.


This uses Underscore's chaining to
  1. Take apart all of the options passed to this function using pairs which will turn { a: 1, b: 2} into [['a', 1], ['b', 2]]

  2. This array of property pairs is filtered by which ones are deemed to be 'actions' in the system.

  3. Then the second index in the array is replaced with a function that returns a promise representing that action (using map)

  4. Finally the call to reduce will merge each "property array" (['a', 1]) back into a final object.

The end result is a transformed version of the options argument, which contains only the appropriate keys and whose values are consumable by the calling function.


Looking at just

.reduce((state, pair) => (state[pair[0]] = pair[1], state), {})

You can see the reduce function starts with an empty state object, state, and for each pair representing a key and value, the function returns the same state object after adding a property to the object corresponding to the key/value pair. Because of ECMAScript 2015's arrow function syntax, the function body is an expression, and as a result, the Comma Operator allows a concise and useful "iteratee" function.

Personally I have come across numerous cases while writing Javascript in a more functional style with ECMAScript 2015 + Arrow Functions. Having said that, before encountering arrow functions (such as at the time of the writing of the question), I'd never used the comma operator in any deliberate way.

try-catch-finally
  • 6,720
  • 6
  • 37
  • 64
Syynth
  • 568
  • 5
  • 10
  • 3
    This is the only really useful answer in respect to how/when can a programmer use the comma operator. Very useful especially in `reduce` – hgoebl Aug 08 '18 at 12:04
  • 1
    Nice answer, but if I may suggest something _slightly_ more readable: `.reduce((state, [key, value]) => (state[key] = value, state), {})`. And I realize this defeats the purpose of the answer but `.reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})` would eliminate the need for the comma operator entirely. – Patrick Roberts Aug 17 '20 at 19:06
  • 1
    While Object.assign is probably more idiomatic these days, or even just the spread operator, I’m not sure they were in widespread use at the time. I would also point out that while the comma operator is a little more obscure, this could generate a lot less garbage in situations where the set of data is very large. The destructuring would certainly help readability though! – Syynth Aug 18 '20 at 23:53
40

The comma operator allows you to put multiple expressions in a place where one expression is expected. The resulting value of multiple expressions separate by a comma will be the value of the last comma separated expression.

I don't personally use it very often because there aren't that many situations where more than one expression is expected and there isn't a less confusing way to write the code than using the comma operator. One interesting possibility is at the end of a for loop when you want more than one variable to be incremented:

// j is initialized to some other value
// as the for loop executes both i and j are incremented
// because the comma operator allows two statements to be put in place of one
for (var i = 0; i < items.len; i++, j++) {
    // loop code here that operates on items[i] 
    // and sometimes uses j to access a different array
}

Here you see that i++, j++ can be put in a place where one expression is allowed. In this particular case, the multiple expressions are used for side affects so it does not matter that the compound expressions takes on the value of the last one, but there are other cases where that might actually matter.

Maël Nison
  • 6,513
  • 6
  • 40
  • 71
jfriend00
  • 580,699
  • 78
  • 809
  • 825
18

Another use for the comma operator is to hide results you don't care about in the repl or console, purely as a convenience.

For example, if you evaluate myVariable = aWholeLotOfText in the repl or console, it will print all the data you just assigned. This might be pages and pages, and if you'd prefer not to see it, you can instead evaluate myVariable = aWholeLotOfText, 'done', and the repl/console will just print 'done'.

Oriel correctly points out that customized toString() or get() functions might even make this useful.

Community
  • 1
  • 1
Julian de Bhal
  • 765
  • 7
  • 11
  • 1
    Ha, very nice idea! (Finally an answer that actually answering the question unlike almost all of the answers {and 3 deleted answers that you need 20K reputation to see...}) – gdoron is supporting Monica Feb 11 '16 at 23:59
  • 1
    If the assigned value is an object, the console may attempt to display it nicely. To do that it might e.g. call getters, which could change the state of the object. Using a comma can prevent this. – Oriol Oct 22 '16 at 11:16
  • @Oriol - Nice! You're totally correct. Somehow this being potentially useful feels just a little bit disappointing :) – Julian de Bhal Dec 05 '16 at 22:17
14

Comma operator is not specific to JavaScript, it is available in other languages like C and C++. As a binary operator this is useful when the first operand, which is generally an expression, has desired side effect required by second operand. One example from wikipedia:

i = a += 2, a + b;

Obviously you can write two different lines of codes, but using comma is another option and sometimes more readable.

taskinoor
  • 43,612
  • 11
  • 111
  • 136
  • 1
    Think this as an alternative, though the definition of good might vary from people to people. However, I can't find any example where you MUST use comma. Another similar thing is ternary ?: operator. That can always be replaced by if-else but sometimes ?: makes more readable code than if-else. Same concept goes for comma as well. – taskinoor Mar 06 '12 at 07:45
  • BTW, I'm not considering the use of comma in variable declaration or initializing multiple variables in loop. In those cases comma is mostly better. – taskinoor Mar 06 '12 at 07:48
  • 3
    this looks confusing af...wtf. – Timmerz Sep 20 '16 at 16:23
7

I'd disagree with Flanagan, and say, that comma is really useful and allows to write more readable and elegant code, especially when you know what you're doing:

Here's the greatly detailed article on comma usage:

Several examples from out from there for the proof of demonstration:

function renderCurve() {
  for(var a = 1, b = 10; a*b; a++, b--) {
    console.log(new Array(a*b).join('*'));
  }
}

A fibonacci generator:

for (
    var i=2, r=[0,1];
    i<15;
    r.push(r[i-1] + r[i-2]), i++
); 
// 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377

Find first parent element, analogue of jQuery .parent() function:

function firstAncestor(el, tagName) {
    while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase()));
    return el;
}

//element in http://ecma262-5.com/ELS5_HTML.htm
var a = $('Section_15.1.1.2'); 

firstAncestor(a, 'div'); //<div class="page">
shaman.sir
  • 2,978
  • 3
  • 25
  • 36
  • 8
    I'm not sure if I would say any of that is **more readable** but it's certainly pretty spiffy so +1 – Chris Marisic Jul 29 '14 at 13:39
  • 2
    You don't need the comma in the while loop in the last example, `while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))` would be just fine in that context. – PitaJ Sep 24 '16 at 04:51
6

There is something "odd" that can be done in JavaScript calling a function indirectly by using the comma operator.

There is a long description here: Indirect function call in JavaScript

By using this syntax:

(function() {
    "use strict";
  
    var global = (function () { return this || (1,eval)("this"); })();
    console.log('Global === window should be true: ', global === window);
  
    var not_global = (function () { return this })();
    console.log('not_global === window should be false: ', not_global === window);
  
  }());

You can get access to the global variable because eval works differently when called directly vs called indirectly.

Community
  • 1
  • 1
Jeremy J Starcher
  • 21,760
  • 5
  • 48
  • 70
6

I haven't found practical use of it other than that but here is one scenario in which James Padolsey nicely uses this technique for IE detection in a while loop:

var ie = (function(){

    var undef,
        v = 3,
        div = document.createElement('div'),
        all = div.getElementsByTagName('i');

    while ( // <-- notice no while body here
        div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
        all[0]
    );

    return v > 4 ? v : undef;

}());

These two lines must to execute :

div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]

And inside comma operator, both are evaluated though one could have made them separate statements somehow.

Sarfraz
  • 355,543
  • 70
  • 511
  • 562
5

I've found the comma operator most useful when writing helpers like this.

const stopPropagation = event => (event.stopPropagation(), event);
const preventDefault = event => (event.preventDefault(), event);
const both = compose(stopPropagation, preventDefault);

You could replace the comma with either an || or &&, but then you'd need to know what the function returns.

More important than that, the comma separator communicates intent -- the code doesn't care what the left-operand evaluates to, whereas the alternatives may have another reason for being there. This in turn makes it easier to understand and refactor. If the function return type ever changes, the code above would not be affected.

Naturally you can achieve the same thing in other ways, but not as succinctly. If || and && found a place in common usage, so too can the comma operator.

  • 1
    Similar to what Ramda\Lodash do with `tap` (https://ramdajs.com/docs/#tap). Essentially, you're executing a side-effect and then returning initial value; very useful in functional programming :) – avalanche1 Mar 08 '19 at 17:43
2

One typical case I end up using it is during optional argument parsing. I think it makes it both more readable and more concise so that the argument parsing doesn't dominate the function body.

/**
 * @param {string} [str]
 * @param {object} [obj]
 * @param {Date} [date]
 */
function f(str, obj, date) {
  // handle optional arguments
  if (typeof str !== "string") date = obj, obj = str, str = "default";
  if (obj instanceof Date) date = obj, obj = {};
  if (!(date instanceof Date)) date = new Date();

  // ...
}
Rich Remer
  • 1,567
  • 13
  • 19
  • 1
    Although I don't favor it myself, this is the only example anyone's given that I think a person could say is better for readability than the equivalent list of individual expression statements without me thinking they're completely insane. – Semicolon May 16 '15 at 15:13
2

Let's say you have an array:

arr = [];

When you push onto that array, you are rarely interested in push's return value, namely the new length of the array, but rather the array itself:

arr.push('foo')  // ['foo'] seems more interesting than 1

Using the comma operator, we can push onto the array, specify the array as the last operand to comma, and then use the result -- the array itself -- for a subsequent array method call, a sort of chaining:

(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
Dexygen
  • 11,681
  • 11
  • 73
  • 144
0

It saves you from using return in nested conditionals and it's very handy especially with the ternary operator. Such as;

function insert(v){
  return this.node > v ? this.left.size < this.right.size ? ( this.left.insert(v)
                                                            , this
                                                            )
                                                          : ( this.left.insert(this.node)
                                                            , this.node = this.right.popmin()
                                                            , this.insert(v)
                                                            , this
                                                            )
                       : this.left.size < this.right.size ? ( this.right.insert(this.node)
                                                            , this.node = this.left.popmax()
                                                            , this.insert(v)
                                                            , this
                                                            )
                                                          : ( this.right.insert(v)
                                                            , this
                                                            )
}
Redu
  • 19,106
  • 4
  • 44
  • 59
0

I just came across this today looking at the proposals for pipeline operator proposal and partial application...

Also, Hack-style pipelines are already feasible without introducing new syntax today:

let $; // Hack-style topic variable
let result = (
  $= books,
  $= filter($, _ => _.title = "..."),
  $= map($, _ => _.author),
  $);

The use of comma expressions here can kind of fake the pipeline operator that isn't in the language yet.

Eliminating the space between $= simulates the feeling of a proper pipe token, |>. Note that the "topic" variable, $, can be anything here and that it's just shorthand for repeatedly overwriting the variable. So something more akin to ...

// blocking inside an IIFE
let result = (() => {
  let $;
  $ = books;
  $ = filter($, _ => _.title = "..."),
  $ = map($, _ => _.author),
  return $;
})()

The "comma" version successfully cuts out some of the noise, getting you closer to what the proposal would be:

let result = books
  |> filter($, _ => _.title = "..."
  |> map($, _ => _.author)

Here's another example using it to compose functions:

const double = (x) => 2 * x;
const add = (x, y) => x + y;
const boundScore = (min, max, score) => Math.max(min, Math.min(max, score));


const calculateScore = ($) => (
  $= double($),
  $= add($, 20),
  $= boundScore(0, 100, $),
  (console.log($), $)
)

const score = calculateScore(28)
Steve Ladavich
  • 2,960
  • 16
  • 26
-3

Another area where comma operator can be used is Code Obfuscation.

Let's say a developper writes some code like this:

var foo = 'bar';

Now, she decides to obfuscate the code. The tool used may changed the code like this:

var Z0b=(45,87)>(195,3)?'bar':(54,65)>(1,0)?'':'baz';// Z0b == 'bar'

Demo: http://jsfiddle.net/uvDuE/

Stephan
  • 37,597
  • 55
  • 216
  • 310
  • 1
    @gdoron Please have a look to this answer http://stackoverflow.com/a/17903036/363573 about the Comma Operator in C++. You'll notice the James Kanze's comment about obfuscation. – Stephan Feb 16 '14 at 18:29