Let's start with this statement:
Most of us, humans, are able to quickly determine the result of simple logical statements and it feels natural to say:
1 < 2 < 3 is true...
3 < 2 < 1 is definitely NOT true.
🔥 BLASPHEMY ! 🔥
...well, not really. 🤷♂️
3 < 2 < 1 is actually TRUE, which makes sense, and you can feel comfortable about it.
But before we jump into a
- What is a logical operator?
- What determines the order of execution of operators
- What is type coercion
After gasping those topics,
3 < 2 < 1 equaling
true breakdown will become silly simple, and very logical. Let's get started!
Logical operators work under the hood just like functions you know, the difference is just lexical (we write them differently). Like functions operators take arguments, execute some logic, and then return something - they also get executed synchronously, one at a time.
Let's look into this simple console example:
2 are the arguments for the function that will check whether one is greater than the other (function logic part) and return a true or false.
Ok. What about
= operator? Well it's a function too! It takes two arguments, it assigns value to the variable (function logic part), and it returns... the assigned value!
Try this in your console:
We simply provided two arguments for the function
=, it did its job using those arguments, and then it returned assigned value. 🤯 Nice.
If you would like to check Precedence (order priority) and Associativity (order direction) for all the JS operators, MDN comes in handy:
Precedence simply orders operators from highest priority to the lowest when we are dealing with a few different operators. (eg.
x = 1 < 2)
Associativity comes in play when you have more than one operator of the same type (eg.
1 < 2 < 3), when precedence is equal JS engine uses associativity to decide on the execution order left-to-right (left side of the statement first), or right-to-left.
For precedence let's break down this simple example first:
x = 2 < 3
< operator has higher precedence (priority) than
=, so the JS engine takes this exact order of steps:
2 < 3 gets resolved first into
x = ... gets resolved
The engine knows
true, because it firstly resolved
< operator even if lexically
= was written before (
x = 2 < 3)
<operator first instead of
=. You may imagine the scenario if there would be no priority order, just the lexical order. I hope this is clear.
Let's look now what happens if precedence is equal due to multiple operators of the same type:
1 < 2 < 3
Here we have two operators
< - two functions executed synchronously one after another. So which one should get resolved first? Precedence is equal, so "associativity" comes into play - for
< operator associativity says
left-to-right, so what happens is:
Step one: engine will first resolve
1 < 2
Step two: engine will resolve, after it resolved first operator
... < 3
(if you remember that
< operator function returns true or false, you may already start seeing where this leads us to 😉, but before we need to explain one last missing element... 👇)
For example: If you have been writing code in JS you are probably aware of "truthy" and "falsy" values - those exist because of the coercion mechanism - any type of data, even
null, can get automatically transformed into true or false, and recognised as such, when the engine needs it.
Here is the wiki definition of coercion I have found:
Let's see the example that is relevant for the final breakdown:
Disclaimer: you should not use Number() function in such a way, I have only used it to prove the point and showcase coercion in action.
true, it will coerce it to number
1. Respectively, if it receives
false, it coerces it to number
0 - computers are zeroes and ones, true or false - makes sense.
We know that
3 < 2 < 1 is built from two functions that will run, synchronously (one at a time), returning values. We know that both functions have the same precedence (priority), so the order of execution is determined by associativity, in this case left-to-right (left side first). Simple. So let's see:
Step one: left side
3 < 2 gets resolved into...
3 < 2 < 1 becomes
false < 1
Step two: type coercion comes into play -
false turns into
false < 1 becomes
0 < 1
0 < 1 returns