The JavaScript Comma Operator

(на русском, 日本)
 
Let’s begin with a funny tweet:

The ‘c’ at the end is for the lowly comma operator. Last in the line of operator precedence and rarely documented, the comma operator hides its light under a bushel. It may not be a JavaScript heavy-hitter but I like it anyway. Its simple, elegant and you should make it your friend. So, here we go – more than you’ll ever need to know about JavaScript’s bashful hero:

What does it do?

The comma operator evaluates both of its operands (from left to right) and returns the value of the second operand. (MDC)

var a = (7, 5);
a; //5

var x, y, z
x = (y=1, z=4);
x; //4
y; //1
z; //4

 
Why did you wrap those variable assignments in parentheses?

Because of operator precedence. A JavaScript statement can contain multiple, disparate operators. The following statement has three operators (*, + and ,) :

return 5 * 2 + 3,  22;

 
Operator precedence determines the order in which operators are evaluated within a statement. The full list, in order of precedence is here. The comma operator has the lowest precedence of any operator. Lets simulate how this applies to the above example:

//original
return 5 * 2 + 3,  22;
//apply * operator
return 10 + 3,  22;
//apply + operator
return 13, 22;
//apply , operator
return 22;

 
Now let’s use that knowledge to see what would happen if we hadn’t wrapped the variable assignment in parentheses:

//original
var a = 7, 5;
//apply = operator
var a, 5; //a is now 7
//SyntaxError: missing variable name 

 
By wrapping the right hand expression in parentheses we create a group – which, effectively has the highest precedence. This ensures that the comma operator gets applied first:

//original
var a = (7, 5);
//apply group
var a = 5; 

 
In practice, lowest operator precedence actually makes the comma operator quite powerful. In effect it says: go ahead and see to all those other little operations first, then watch me come and clobber the result.

Some statements contain multiple commas. How does that work?

The above rule still applies. Each comma operator in the statement is processed in sequence from left to right.

var a = (1, 2, 3, 4);
a; //4

 
This is equivalent to:

var a = (((1, 2), 3), 4);
a; //4

 
What about commas used in type literals and declarations?

These are comma separators not comma operators. The purpose of a comma separator is to delimit members in a list. For example:

//set 4 array elements
var arr = [1, 2, 3, 4];

//create an object with 2 properties
var obj = {
  a: 22,
  f: function() {return this.a*this.a}
}

//define 3 distinct variables
var a = 1, b = 2, c = 3;

//invoke a function passing 2 arguments
Math.max(4, 7);

 
Why use comma operators?

Because they let you specify more than one expression where JavaScript expects only one. Comma operators are rarely essential but often useful and just occasionally downright elegant:

var r = [], n = 0, a = 0, b = 1, next;

function nextFibonacci() {
    next = a + b;
    return b = (a = b, next);
}

while(n++ < 10) {
    r.push(nextFibonacci());
}

r; //[1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

 

function getRandomPrime() {
    while(n = Math.round(Math.random()*1000000000), !isPrime(n));
    return n;
}

var isPrime = function(n) {
    d = Math.ceil(Math.sqrt(n));
    while(n%(d--) && d);
    return !d;
}

getRandomPrime(); //425593109
getRandomPrime(); //268274719

 
Isn’t the comma operator just a semicolon in disguise?

Semicolons partition statements. Comma operators partition expressions within statements.

Why wouldn’t I just use the && operator to evaluate multiple expressions sequentially?

The comma operator is a close cousin of the && and || operators. All three operators will return the last expression they evaluate. The distinction is straightforward:

//(LHE: left hand expression, RHE right hand expression)

LHE && RHE
1. Always evaluate LHE
2. If LHE is true, evaluate RHE

LHE || RHE
1. Always evaluate LHE
2. If LHE is false, evaluate RHE

LHE, RHE
1. Always evaluate LHE
2. Always evaluate RHE

 
Choose the comma operator when both expressions must always be evaluated.

How about some more examples?

Okay. Earlier on I mentioned that comma operators let you specify more than one expression where JavaScript expects only one. This is perhaps most useful within the confines of the for loop:

for loops

Here’s an alternate version of a fibonacci generator, also using the comma operator:

for (
    var i=2, r=[0,1];
    i<15;
    r.push(r[i-1] + r[i-2]), i++
); 

r //"0,1,1,2,3,5,8,13,21,34,55,89,144,233,377" 

 
For another example, consider a utility that helps a store clerk select the bills and coins that make up a customer’s change. Here’s the basic version. We use a comma operator to bisect the second statement of the for loop. This lets us neatly increment our currency counter before testing against the limiting expression:

 
function toCurrency(total, values) {    
    total *= 100;     
    for(        
        var i=0,counts=[];
        counts[i]=total/values[i], total=total%values[i];
        i++
     );     
     return counts.map(Math.floor); 
} 

toCurrency(32.47, [500, 100, 25, 10, 5, 1]); //[6, 2, 1, 2, 0, 2]

 
Now here’s the same utility with added formatting for user-friendliness:

 
function toCurrency(total, values, sym) {
    total *= 100;     
    //do the calc     
    for(
        var i=0,counts=[];
        counts[i]=total/values[i], total=total%values[i];
        i++
    );     
   //format
   var results = counts.map(function(s,i) {
       return s>=1 && [Math.floor(s),"x",(sym || '$') +
            (values[i]/100).toFixed(2)].join(' ');
    });
    return results.filter(Boolean).join(', ');
}

toCurrency(19.77, [500,100,25,10,5,1]);
//"3 x $5.00, 4 x $1.00, 3 x $0.25, 2 x $0.01"
toCurrency(19.77, [500,100,50,20,10,5,1], '£');
//"3 x £5.00, 4 x £1.00, 1 x £0.50, 1 x £0.20, 1 x £0.05, 2 x £0.01"
toCurrency(19.77, [500,100,50,20,10,5,2,1], '€');
//"3 x €5.00, 4 x €1.00, 1 x €0.50, 1 x €0.20, 1 x €0.05, 1 x €0.02"

 
This following function uses the comma operator to simultaneously increment and decrement two counters within a for loop. The product of the counters is used to render a rather fetching curve in the console:


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

renderCurve();
/*
*********
*****************
***********************
***************************
*****************************
*****************************
***************************
***********************
*****************
*********
*/

 
while loops

You can use a comma operator to create a succinct version of the do-while loop. This routine searches an elements ancestry looking for a tag name match. Again we use the comma to perform an action prior to checking the limiting expression:

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">

 
Ternary conditionals

Ternary syntax allows for only one statement in each of its three components. As a general rule, if you need to use more statements you should consider using if else instead. However it’s sometimes more readable when the comma operator is used to combine short succinct expressions within a ternary statement:

//player loses
lives ? (lives--, go()) : (gameOver(), exit());

 
Debugging

The comma operator provides an unobtrusive way to inject console logs into your code without having to reformat (can you spot the errors that necessitated debugging in each case?)…

//CONTAINS AN INTENTIONAL ERROR!!!
//sum products while i > n
var i=10, n=0, total=0;
while(console.log(i,n), i-- > n++); {
    total += i*n
}
//CONTAINS AN INTENTIONAL ERROR!!!
//sum an array
var arr = [1,2,3];
for (
    var i=0, total=0;
    i<arr.length;
    console.log(i,total), total += arr[i++]);
)
//CONTAINS AN INTENTIONAL ERROR!!!
//add 4 to members of array and sum it
//(yes there are easier ways to do this!)
var testArray = [3, 5, 8, 4], total = 0;
var plusFour = testArray.map(function(e) {e + 4})
plusFour.forEach(function(n) {console.log(n), isNaN(n) || (total += n)});

 
Binding with iterators

@wavded posted this nifty technique for unobtrusively resetting iterators. Again, you don’t need to do it this way – but the tidiness appeals to me:

var colorIndex = 0, 
    colors = ["FF0000", "008000", "FF0086", "A2FF00", "0000FF", "800080"]; 

function selectNextColor(){
    return colors[colorIndex++] || colors[colorIndex = 0, colorIndex++];
}

 
Indirect calls to eval

eval¹ calls are normally invoked within their containing context (i.e. the this value in the evaluated code will be the same as the the this value of the surrounding code). This is problematic since there is no guarantee that repeated eval calls will originate in the same context.

As @kangax describes here, we can use the comma operator to fashion an indirect call to eval which will force it to execute in the global context²:

var a = {};

//attempt eval in context of object <code>a</code>
(function() {
    eval("this.alert('If you can read this I must be global!')");
}).call(a);
//TypeError: this.alert is not a function

//force eval in global context
(function() {
    (0,eval)("this.alert('If you can read this I must be global!')");
}).call(a);
//alerts: 'If you can read this I must be global!'

 
¹ discussion of the merits of eval are beyond the scope of this article 😉
² although the ES5 standard confirms that indirect calls to eval should run in the global context, not every browser is compliant (i.e. IE <= 8).

Wrap Up

You could probably write perfectly good JavaScript code without ever using the comma operator. Does this mean I just wasted your time? I hope not. Just as an extensive vocabulary makes us better speakers and writers, so a comprehensive access to language features should make us better coders. The more techniques we have at our disposal the greater our ability to write elegant, succinct and readable code. Have fun with comma operators and please share your neat usage examples!

Further Reading

ECMA-262 5th Edition
    11.14 The comma operator
    10.4.2 Entering eval code
    15.1.2.1.1 Direct Call to Eval

Mozilla Developer Center
    comma operator
    operator precedence

Juriy Zaytsev (@kangax): global eval, what are the options
Mark Harter (@wavded): cycling through an array using the comma operator

59 thoughts on “The JavaScript Comma Operator

  1. Great article. I mean a comma an operator. Who would have thought. I still think I’ll avoid this as writing code that is understood by 3 developers in the world is not cool but great read none the less.

  2. As long as the comma operator isn’t used in ways that it can produce assignment side effects it certainly has it’s uses. I like to use the comma operator in declarations/assignments, but do not think this article justifies use of comma operator for side effects, eg.

    var x = (4, 2); // 4 is just discarded
    var y = (foo(), bar()); // foo and bar is run, return value of bar is assigned to y

    IMO, the latter is better written as

    foo();
    var y = bar();

    enven though it’s an extra line. Keeping maintainability and readability in mind that is.

    1. >> var y = (foo(), bar());
      >> IMO, the latter is better written as
      >> foo();
      >> var y = bar();

      The value of the comma operator comes from when you want to communicate to the human reader that the calls to foo and bar are intimately bound (and typically order dependent).

      For example, a common C++ idiom is

      delete p, p = 0;

      One statement, comprising two expressions that are intimately bound and can be considered one logical state change.

      It makes very little difference to the interpreter/compiler, whether you use a semi-colon or a comma, but the phrasing is done for the human reader.

      So yes, if foo and bar are unrelated, I’d split them, whereas if I found code with them in the first form, I’d expect to find that they’re logically bound in some way (maybe foo() performs some initialisation that bar() relies on).

      1. Could you just use a semicolon within the same line to achieve the same effect? It makes for easier reading because it can’t be mistaken for a comma “separator” as in a param list.

        delete p; p = 0;

      2. I respectfully disagree and would probably never hire a programmer that did this. First off, I would personally never think they were reliant on each other after reading that unless as a team we decided and documented that is what it means (which personally, with the complexity it adds and as little as the operator is known, there would be better ways to do this). Instead, I would think I hired a lazy programmer that can’t be bothered to write reading friendly code. Second, of you have a situation where bar relies on foo being called first, than its probably programmed wrong to begin with if it has that much dependency and should be rewritten.

  3. A little (and silly) example if you want assign an array length in a for loop:

    a = [1,2,3,4];
    for ( i = 0; i < ( length = a.length, length ); i++ ) {
    console.log(a[i]);
    }

    1. Bad example.
      I believe your assignment is going to be evaluated on every iteration.
      A better one would be:

      a = [1,2,3,4];
      for (var i = 0, length = a.length; i < length; i++ ) {
      console.log(a[i]);
      }

      1. Agree the first example is inefficient
        Your example is better but doesn’t use comma operator – you are parsing variable section which uses the comma as a separator not an operator

      2. guessing it could be useful if you could use it to execute other code part way through a long cascade – something vaguely like

        (var myLink = new Link())
        .href("www.wordpress.com/"+sublink)
        .top("150px")
        .tooltip((sublink.replace("?",""),sublink))
        .fontSize("1.5em")
        .content(sublink);

        (if you had a link object with those methods)
        ps: sorry if i’m a bit late to the discussion 😉

  4. @peng @guido thanks! glad you like it

    @anders I agree that with commas you need plenty of self-discipline to stay clear of obfuscation hell. We may agree to differ on exactly where to draw the line

  5. Got to agree with Anders this seems like being clever for clever’s sake, it doesn’t seem to produce more elegant code as it would be unnecessarily confusing to most.

    1. Not sure I agree. Comma operators are used in jQuery and mootools. Pretty sure they’re in prototype.js too (haven’t had time to verify). These are successful collaborative development environments.

      Even if you don’t want to use comma operators yourself – the fact that the major frameworks use them means its probably good concept to master.

      Again comma operators should be used sparingly and with full consideration for readability vs ambiguity. I see it as just another tool to use with discretion

      1. The terminology “successful” is relative in this context. WordPress is successful but has a bad codebase. I’m quite sure there are tons of popular (and being used in real life/business/production-level environment) open source software with less desireable codebase out there.

        Open Source software typically has less concern: here’s the codebase, do whatever you like with it thus the code readability and quality are a mix bag (sometime a mix bag of hurt).

        jQuery and other JS libraries are useful for me. Would I read the internal code? heck no. I’d rather someone else figure it out and fix the bugs for me.

      2. It’s up to you whether you read JQuery code, but JQuery developers read other JQuery developers’ code and, last I heard, they’re not at each others throats over the fact it has comma operators in it.

      3. @Ted: In my experience, quite the opposite is true. Open Source software lives and dies by the ‘quality’ of its codebase. IMO, the jQuery source is highly readable, particularly in comparison to some of the closed-source codebases I’ve seen.

  6. Hey Angus,

    You may want to mention the following interesting confusions related with comma operator:

    var a = 10;
    var b = 20;
    
    a, b = b, a;
    
    // what "a" and "b" after the operation and why?
    
    console.log(a, b); // ? ?

    And also (which was on of the my quiz’s questions):

    var a = (1,5 - 1) * 2
      
    console.log(a); // ?

    Dmitry.

    1. @Angus: Great article.

      The comma operator is not always an operator. When separating variables in function argument’s it does not apply.

      @Dimitry
      a, b = b, a;

      It’s just an order of operator precedence:
      1. the parser interprets from LTR
      2. left comma-op evaluates to ‘b’
      3. = takes now precedence, and is assigned the righthand-statement ‘b’
      4. right comma-op evaluates to ‘a’ (which is why the value of a is returned on the command-line when writing the statement)

      Hence in the example nothing a, b = b, a changes. The expression could be reduced to:
      b, a;

      var a = (1,5 – 1) * 2

      keyword var gets resolved, so that the evaluated variables are put-on / or flagged for the local stack.
      a is declared, (1,5 – 1) eval’ed: (1,4) *2 -> 4 *2 -> 8, = assignment

  7. Shouldn’t it be `lives ? (lives–, go()) : (gameOver(), exit());` ?

    Anyway, good article that needed hashing out. I agree that this requires a lot of self-discipline or it could get well out of hand.

  8. Well, I doubt if this was your intention, but your examples convinced me that there really is no compelling reason to ever use the comma operator at all. It should go in the same trash bin as ‘==’

  9. Great article!

    I once needed to use the comma operator to stop a (huge) application from triggering the long-running script warning in Internet Explorer. Where other browsers track how long a script has been running, IE used to keep track of the total number of statements executed. Combining multiple operations in a single statement with a comma can be just what you need sometimes.

  10. Very impressive. I’ve used comma operator sometimes in for loop, but I haven’t understand it much. This post is great. Thank you so much for this.

  11. I am not a fan of JS at all. The best we can hope is that JS will be hidden and used as a browser machine language. JS can then be generated by proper languages.

  12. I agree with @ jared above.

    Admittedly, I will probably start using some of these techniques in my code. However, it can be difficult to find a balance between very efficient code and readable/maintainable code. Often times I will forgo an implementation of super efficient code for a more verbose implementation. If only for the sake of others debugging my code. Of course, you could make the case that other developers should be as skilled as yourself or you shouldn’t commit buggy code in the first place, but in most work places that is not reality.

    Anyway, thanks for the post. Very cool stuff none the less.

    1. @jcipriano Fair point

      I guess the only thing that gets me is this: if we can understand && and ||, why is it such a stretch for all of us to understand their sibling comma operator? Operators don’t get any simpler. Meanwhile typical production code is full of heavily nested namespaces, chaining anonymous functions and complex hierachies. Those things seem like more serious obfuscation issues.

      But bottom line is do whatever you and your team feel comfortable with. As I said you can write perfectly good JavaScript without ever using a comma operator.

  13. Nice one 🙂

    Maybe you could also explain why these are different: x=y; x=(y); x=(y,y). So how comma actually fetches it’s operands thus losing its context (especially important with object methods). So (this.x,this.y)(); will probably fail, even though the right method is called.

    I thought it would come after the eval examples, but didn’t see it 🙂

  14. return colors[colorIndex++] || colors[colorIndex = 0, colorIndex++]; ???

    should be : return colors[colorIndex++] || colors[colorIndex = 0];

    1. @Anonymous if so, then you would pick up 2 times in a row the 0-indexed element, as the colorIndex++ instructions returns the pre-increment value.

  15. Hi Angus, nice post!

    BTW, in getRandomPrime() I guess you would like to have while(var n =…) not to introduce it to global scope 😉 Same with d -> var d in isPrime.

    Regarding the previous comment from Anonymous: the code is fine now. Your change would make the color at index 0 be returned twice as frequently than the others.

    This is the other possibility:
    return colors[++colorIndex] || colors[colorIndex = 0];
    but it would start returning from colors[1] instead of colors[0].

  16. Wow! You blew my mind. Cool examples, great resource. FYI, your prime calculator will compute faster is you do this instead:

    var isPrime = function(n) {
    max = Math.ceil(Math.sqrt(n));
    d=2;
    while(n%(d++) && d<=max);
    return (d<=max);
    }

  17. Help me please, how to created javascript for total result contain multiple commas.

    For example;

    contain in javascript is:
    6,12,3,6,7,9,43,21,5,9,2,71,103,257

    How to result :
    14

    Regards
    Tenkup

  18. I think this is elegant for a one-way switch.
    var initialized = false;
    initialized = initialized || (init(), true);

    Even if you aren’t familiar with the comma operator (which you should be even if you do not use it, ignorance is no excuse), I think it’s fairly obvious what it does.

    1. Wouldn’t your example be better if init() just returned true instead?
      initialized = initialized || init();
      That way if for some reason init() failed, the flag would remain false.

  19. Everything is very open with a clear clarification of the challenges.
    It was definitely informative. Your site is useful.
    Thank you for sharing!

Leave a reply to Anonymous Cancel reply