The Comma Operator and Operator Precedence in JavaScript
Tags: javascript, comma, operator, precedence
Published: 2019-11-27 20:05:00

Let's take a look at an expression. Give you best guess for what it evaluates to:

```js
    40 + 36,000
```

Some might say `76000`, others might say it'll throw an error. In actuality, this expression is completely valid in JS and it evaluates to `0`. How? The comma operator.

The comma operator is the operator with the weakest "binding" in JS. Remember PEMDAS from math class?  Operators have their own order of operations, but it's instead referred to as bindings or ["Operator Precedence"](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence).

MDN describes precedence as "Operators with higher precedence become the operands of operators with lower precedence". What this means is that if an operator (which has 2 operands) has a higher precedence, it is as if it is surrounded by  parentheses; it is more strongly *bound* to the values to its right and/or left.  

```js
    40 + 36,000                          // 0
    (40 + 36) , 000                      // 0

    new Date().getDay() + 3 * 2          // some number  6 - 12
    (((new Date()).getDay)()) + (3 * 2)  // the same number
    
    ! void false && true || false        // true
    !((void (false && true)) || false)   // true
    
    true ^ ! 100 < 56 ? 2 : 1             // 3
    true ^ (((! 100) < 56) ? 2 : 1)       // 3
```

The levels of precedence for each is listed on [the MDN page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) near the bottom,  right  after the example. If 2 operators have the same precedence, their associativity tells us whether to go right-to-left or left-to-right when evaluating them.

The comma operator has the lowest precedence. It binds last. If you ever see a comma, you know it won't accidentally bind to some code that it shouldn't. 

So what does the comma do? It takes 2 expressions as operands and returns the rightmost one. It is left-to-right associative and we can therefore chain them. Here are some examples

```js
    5 , 2               // 2
    3 , 4 * 2           // 8
    40 , 5 , 1 , false  // false 
```

Note that it specifically takes in expressions, not statements. That means we *can't* put things like `let x = 4` as one of the operands, but we *can* put things like `x = 4`. Declarations are statements, while assignments are expressions.

What else is an expression? decrements, increments, and Function calls!

This means the following is valid JS:

```js
    let x;
    const myFunc = num => {
    	for (let i = 0; i < num; i++) console.log(i);
    } 
    
    const result = (x = 3, x++, myFunc(x), x * 2);
    console.log(`result: ${result}`);
    
    // logs:
    // 0
    // 1
    // 2
    // 3
    // result: 8
```

Parentheses are required as the comma operator's low precedence would make us accidentally combine the assignment and the first expression.  Parentheses have the highest precedence, so they contrast nicely to the comma's lowest precedence.

The one case where parentheses are not required is when  one approaches an "operator" of even  higher precedence, the semicolon. 

This allows us to leave the parentheses behind where semicolons are involved, such as in  loop headers.

```js
    let i;
    for (i = 0, i++, i--; i < 10, i < 5; i += 2, i-= 1) {
    	console.log(i)
    } 
    
    // logs:
    // 0
    // 1
    // 2
    // 3
    // 4
```

How is any of this useful? One of my favorite way is [code golfing](https://dev.to/emnudge/js-code-golfing-how-to-ruin-everyone-s-day-40h3). As long as we don't involve any statements, we can stuff an entire loop body into the header.

```js
    for (let i = 0; i < 3; console.log('hey!'), i++);
    
    // logs:
    // hey
    // hey
    // hey
```

If we have a variable that is already defined and initialized, we can also just use a while loop.

```js
    let i = 0;
    while(console.log('hey!'), ++i < 3);
    
    // logs:
    // hey
    // hey
    // hey
```

One of the situations which the comma operator *would* have been especially useful is for a replacement for a function body when it comes to arrow functions. Unfortunately, the  comma operator has an even lower precedence than `=>` and so we require parentheses to separate them. This means we're not saving any  characters, as we're using `()` and `,` instead of  `{}` and `;`. 

It is only useful in arrow functions if you intend on returning a value, in which you are missing out on  a `return` statement due to the arrow function's implicit return when no function body is present.

```js
    const myFunc1 = () => (console.log("func1 called"), 3);
    const myFunc2 = () => {console.log("func2 called"); return 3}
    
    console.log(myFunc1());
    console.log(myFunc2());
    
    // logs:
    // func1 called
    // 3
    // func 2 called
    // 3
```

# Consclusion

I hope you learnt a bit more about operator precedence and the power of manipulating  it. I often see people grouping operators where they otherwise wouldn't have to. Grouping operators can often make code more readable, but too many can just as well bring about expression and operator soup.

Use your new found power wisely.