Why JavaScript's == Doesn't Always Behave as You Expect

Let's say we have a number, 20, and a string, "20". We compare them loosely using ==:

const numVal = 20;
const stringVal = "20";
console.log(numVal == stringVal);

What do you expect? Should it be true or false?

It turns out to be true.

Now, let's look at another example. We have a boolean, true, and a string, "true". Again, we compare them loosely:

const boolVal = true;
const stringVal = "true";
console.log(boolVal == stringVal);

What do you expect to see? true or false? It logs false.

So why does it behave differently in the first example compared to the second?

Let me walk you through how JavaScript's loose equality (==) really works.

When we use == to loosely compare values in JavaScript, if the types are different, both values are coerced to the same type and then compared to see if they have the same value.

The general rule of JavaScript's coercion is simple: "If the types aren't the same, try to compare them as numbers." Let's look at some examples to see how this works.

Comparing a String to a Number

When you compare a string to a number, JavaScript tries to convert the string into a number first:

const numVal = 10;
const stringVal = "10";
console.log(numVal == stringVal); // true

Here, "10" is converted to the number 10, so the comparison evaluates to true.

Comparing a Number to a Boolean

When you compare a number to a boolean, JavaScript converts the boolean to a number. In JavaScript, true is 1 and false is 0, so the comparison results in true:

const numVal = 1;
const boolVal = true;
console.log(numVal == boolVal); // true

Comparing a String to a Boolean

When comparing a string to a boolean, JavaScript first tries to convert both values to numbers:

const stringVal = "1";
const boolVal = true;
console.log(stringVal == boolVal); // true

Here, "1" is converted to the number 1, and true is also converted to 1, so the comparison returns true.

However, JavaScript is quite literal when converting values. It won’t try to interpret words like "twenty" as numeric values:

const stringVal = "twenty";
const numVal = 20;
console.log(stringVal == numVal); // false

In this case, "twenty" is converted to NaN, and NaN is not equal to any number (not even itself). So, the comparison evaluates to false:

console.log(NaN == 20); // false

Exceptions

There are a few exceptions to this rule. For example, null and undefined are only loosely equal to each other, but not to any other values:

console.log(null == undefined); // true
console.log(null == 0); // false
console.log(undefined == false); // false

Also, symbols behave like objects because they have reference identity:

const sym1 = Symbol("id");
const sym2 = Symbol("id");
console.log(sym1 == sym2); // false

Get my free, weekly JavaScript tutorials

Want to improve your JavaScript fluency?

Every week, I send a new full-length JavaScript article to thousands of developers. Learn about asynchronous programming, closures, and best practices — as well as general tips for software engineers.

Join today, and level up your JavaScript every Sunday!

Thank you, Taha, for your amazing newsletter. I’m really benefiting from the valuable insights and tips you share.

- Remi Egwuda