DEV Community

loading...
Cover image for JavaScript logical operators will never confuse you again. '3 < 2 < 1' equals true explained! πŸ’‘

JavaScript logical operators will never confuse you again. '3 < 2 < 1' equals true explained! πŸ’‘

matborowiak profile image Mateusz Borowiak ・5 min read

JavaScript is a subject of many internet jokes and memes. It is often criticised for its so-called "weird behaviour". Source of those accusations is often associated with how JavaScript resolves its operators, usually the logical operators.

Let's start with this statement:
How humans read things !== how JavaScript reads things.

Most of us, humans, are able to quickly determine the result of simple logical statements and it feels natural to say:

While 1 < 2 < 3 is true...
... 3 < 2 < 1 is definitely NOT true.

And here comes in JavaScript:

πŸ”₯ BLASPHEMY ! πŸ”₯

...well, not really. πŸ€·β€β™‚οΈ

In this article I will try to explain to you using very simple examples what in fact JS operators are, and how JavaScript engine reads those, and by the end, 90% of JavaScript memes will lose it's laugh potential, as you are about to discover that 3 < 2 < 1 is actually TRUE, which makes sense, and you can feel comfortable about it.

But before we jump into a 3 < 2 < 1 breakdown, we need to understand a few fundamentals on how JavaScript engine reads and executes your statements. For that we need to know:

  1. What is a logical operator?
  2. What determines the order of execution of operators
  3. 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!

1. Logical operators are functions returning values!

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:

1 and 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.

2. Operator Precedence and Associativity (the order of actions)

If you would like to check Precedence (order priority) and Associativity (order direction) for all the JS operators, MDN comes in handy:

MDN Operator Precedence

Operator precedence and associativity, using simple words, are concepts used to determine the order for a JavaScript engine in which it will resolve your operators.

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:

x = 2 < 3

Step one: 2 < 3 gets resolved first into true
Step two: x = ... gets resolved

The engine knows x equals true, because it firstly resolved < operator even if lexically = was written before (x = 2 < 3)

Because of the precedence, javascript knows it needs to start by resolving < 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:

1 < 2 < 3

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... πŸ‘‡)

3. Type coercion

Type coercion is one of the core JavaScript mechanisms that work under the hood. Simply saying, coercion implicitly (automatically) turns one value type into different type when needed.

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:
Wikibooks Coertion

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.

What you can see here is when the JavaScript engine needs the number, but receives 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.

4. The fun part: 3 < 2 < 1 equals true breakdown

Well for humans that's definitely not true. But knowing all above lets see how the JavaScript engine reads this statement, and if this makes sense or not...

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:

3 < 2 < 1

Step one: left side 3 < 2 gets resolved into... false

3 < 2 < 1 becomes false < 1

Step two: type coercion comes into play - false turns into 0

false < 1 becomes 0 < 1

Step three: 0 < 1 returns true!

3 < 2 < 1 equals true. Is JavaScript broken then? Absolutely not.

If you look into the steps we went through, you will be able to break down and logically (πŸ˜‰) explain many of the examples posted on the internet implying JavaScript is "weird" or "broken". There are still some actual parts weirdly behaving or designed such way, and for different reasons - but firstly, there is no perfect programming language existing yet, and secondly, there is little JS-meta-weirdness left as most it you can now explain.

Discussion (1)

pic
Editor guide
Collapse
roterski profile image
Piotr Roterski

(> 3 2 1)
true

(< 3 2 1)
false

I certainly do not miss js quirks after migrating to clojurescript

repl.replete-web.io/