A statement or an expression in any programming language could involve some arithmetic calculations with more than one operation. For instance, consider the following statement:
It has two operators (+ and * respectively), and in order to evaluate the final result, you need to perform these two operations.
You can either perform the addition operation first, followed by the multiplication operation.
Or you can do it the other way round by performing the multiplication first, followed by the addition operation.
In both cases, you will get different results due to the order in which each operation is executed. So how do you determine the correct sequence of operations to produce consistent results in any arithmetic calculation?
Precedence Level for Common Operators
Note that precedence level varies from 1 to 19. The general rule is that an operator that has a lower precedence level is given higher priority and is said to have higher precedence than the one having a higher precedence level. You can interpret precedence level as rank in priority level of each operator.
|Division||/||3||Left to Right|
|Multiplication||*||3||Left to Right|
|Modulus||%||3||Left to Right|
|Addition||+||4||Left to Right|
|Subtraction||-||4||Left to Right|
|Assignment||=||14||Right to left|
|Exponentiation||**||14||Right to left|
So if we look at the above table, multiplication will have higher precedence than addition. Similarly, the division will have higher precedence than subtraction. However, addition and subtraction have the same precedence. Similarly, multiplication and division also have the same precedence.
We also have a column for associativity. So let's understand what it means.
Associativity defines the direction to be followed when executing the operations in a statement. It comes into the picture when statements contain operators that have the same precedence. In order to understand completely why associativity is even needed in the first place, let's take a look at the following example.
Need for Associativity
Consider the below expression.
We know both multiplication and division have the same precedence.
So how should you evaluate the expression from the left side, i.e., do you perform division first?
In that case, as you can see, the output would be 4.
But if you evaluate the expression from the right side, ie, perform multiplication first and then division:
The output would be 1. Since we get different outputs in both cases, we need the rule to resolve this ambiguity. That's exactly where associativity comes in.
Left to Right Associativity
The associativity of an operator describes the direction in which the operations would get executed within a statement.
Right to Left Associativity
In contrast to the left to right associativity, some operators may have right to left associativity.
In the table we saw earlier, the assignment operation had the right to left associativity. Let's consider the following example where a value is assigned to three variables.
Since the assignment follows right to left associativity, 25 is first assigned to the variable z. Then, the value of variable z (i.e., now 25) is assigned to variable y. Finally, the value of variable y, which is now 25, is assigned to variable x. Here's how the right to left associativity will break down the above statement:
It's simple to look at the above statement and conclude that we're only assigning the same value to three variables x, y, and z. However, it's imperative to understand how the assignment is actually happening.
If you think the result would be the same even if we evaluate the expression from left to right, think again!
Let's say we evaluate the expression from the left side first. Here's how we'll break the statement down:
First, the value of y will be assigned to x. But since y doesn't have a value yet, x will get undefined.
Similarly, y will also get undefined in the next step since z also doesn't have any value yet.
Finally, z will be assigned a value 25. But at the end, both x and y will have the value undefined in contrast to the right to left evaluation, where both had a value 25.
Let's say you're evaluating the following expression.
The foremost thing to do is to identify which operators have the highest precedence. We already know multiplication and division have higher precedence than addition and subtraction.
So now, we'll evaluate the division and multiplication operations first. Since their associativity tells us that the expression must be evaluated from left to right, we'll evaluate division first.
Followed by multiplication:
Once we have narrowed down the expression, we'll again look at the precedence of the operators to determine which operation needs to execute first. And then, if there's a conflict in the precedence, we let their associativity break the tie.
We already know the precedence and associativity of common operators, but what about the other remaining operators?
Here's a table that lays down each operator, along with it's precedence value and associativity type:
|Operator||Operator Use||Operator Associativity||Operator Precedence|
|0||Method/function call,grouping||Left to right||Highest -1|
|||Array access||Left to right||1|
|.||Object property access||Left to right||1|
|++||Increment||Right to left||2|
|--||Decrement||Right to left||2|
|-||Negation||Right to left||2|
|!||Logical NOT||Right to left||2|
|-||Bitwise NOT||Right to left||2|
|delete||Removes array value or object property||Right to left||2|
|new||Creates an object||Right to left||2|
|typeof||Returns data type||Right to left||2|
|void||Specifies no value to return||Right to left||2|
|/||Division||Left to right||3|
|*||Multiplication||Left to right||3|
|%||Modulus||Left to right||3|
|+||Plus||Left to right||4|
|+||String Concatenation||Left to right||4|
|-||Subtraction||Left to right||4|
|»||Bitwise right-shift||Left to right||5|
|«||Bitwise left-shift||Left to right||5|
|>.>=||Greater than, greater than or equal to||Left to right||6|
|<,<=||Less than, less than or equal to||Left to right||6|
|==||Equality||Left to right||7|
|!=||Inequality||Left to right||7|
|===||Identity operator - equal to (and same data type)||Left to right||7|
|!==||Non-identity operator - not equal to (or don't have the same data type)||Left to right||7|
|&||Bitwise AND||Left to right||8|
|^||Bitwise XOR||Left to right||9|
||||Bitwise OR||Left to right||10|
|&&||Logical AND||Left to right||11|
|Il||Logical OR||Left to right||12|
|?:||Conditional branch||Left to right||13|
|=||Assignment||Right to left||14|
|*=,/=,%=,+=,,==,<<=,>>=, >>>=, &=,^=,1=||Assignment according to the preceding operator||Right to left||14|
Now you can use the above table to validate all the examples demonstrated previously.
We have Bitwise XOR, the modulus operator, and the exponentiation operator as well.
Here's how we'll evaluate it:
We evaluate the expression from left to right and carry out the XOR operation first. Then, we evaluate the exponentiation operation because of right to left associativity. Then we evaluate the modulus operation because the modulus operator has higher precedence than subtraction which gives us the result 20.
Grouping and Short Circuiting
If you look at the table again, you'll notice that the grouping operator () has the highest precedence.
This means that the given expression will evaluate to 16 and not 13:
Note that while demonstrating which operator will be evaluated first, I have manually applied the grouping operator in all the previous examples. That was simply an attempt to distinguish the operation being performed at that instance from the rest of the operations present in the expression.
If the grouping operator has the highest precedence, what do you think will happen in the following statement:
Here, the above expression will evaluate false. Well, you might think that since we have a grouping operator, the addition operation is evaluated first, and then the conditional or logical AND operator is evaluated. However, that's not the case here.
This is due to a concept called short-circuiting. In the above statement, we know false is not true. So the statement or expression is short-circuited at that point, and the grouping operator is not evaluated at all.
- The grouping operator is assigned the highest level of precedence.
- Short-circuiting allows conditional operators to take precedence over the grouping operator.