Understanding equality in JavaScript
Tags: javascript, node, webdev, howto
Published: 2022-11-28 18:12:10

We have all been there… You compare two values using `==`, receive a totally unexpected result and wonder _WT*_ is going on?! - Just Equality `==` operator at its finest.

Equality `==` operator has the following syntax.

```javascript
x == y
```

It takes two operands, `x` and `y`, converts them into the same type, compares them by value and returns a `Boolean` result.

> Always use Strict Equality `===` operator. There is literally no benefit in using `==` instead of `===`, except if your goal is to introduce bugs to your codebase.

But you did not come here so I can tell you to start using `===`. Just look at the code below. You want to know how it can possibly be explained, don't you? - And yes, you should know it. After all, you are working with JavaScript and this is a JavaScript feature.

```javascript
'' == '0'             // Result: false
0 == ''               // Result: true
0 == '0'              // Result: true

false == undefined    // Result: false
false == null         // Result: false
null == undefined     // Result: true

[] == 0               // Result: true
[] == '0'             // Result: false
['a', 'b'] == 'a,b'   // Result: true

NaN == NaN            // Result: false
'\n\r\t' == 0         // Result: true
```

## IsLooselyEqual algorithm

When you use `==`, under the hood, this algorithm kicks in and gives the result of comparison. So, yes, here lies the cure for our pain.

This algorithm is described in [ECMAScript specification](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-islooselyequal) and basically every [JavaScript engine](https://en.wikipedia.org/wiki/JavaScript_engine) has to implement it as described.

It is common to represent algorithms with some kind of graph or flow chart, but since I am very bad at drawing, I present to you this magnificent [pseudocode](https://en.wikipedia.org/wiki/Pseudocode).

```text
Take two operands, `x` and `y`, as an input

If type of `x` is the same as type of `y`
  If type of `x` is `Undefined`
    return `true`
  If type of `x` is `Null`
    return `true`

  If type of `x` is `Number`
    If `x` is `NaN`
      return `false`
    If `y` is `NaN`
      return `false`
    If `x` is the same as `y`
      return `true`
    If `x` is `-0` and `y` is `+0`
      return `true`
    if `x` is `+0` and `y` is `-0`
      return `true`
    return `false`

  If the type of `x` is `String`
    If `x` and `y` are exactly the same sequence of characters
      return `true`
    return `false`

  If the type of `x` is `Boolean`
    If `x` and `y` are both `true`
      return `true`
    If `x` and `y` are both `false`
      return `true`
    return `false`

  If `x` and `y` refer to the same object
    return `true`

  return `false`

If `x` is `null` and `y` is `undefined`
  return `true`
If `x` is `undefined` and `y` is `null`
  return `true`.

If type of `x` is `Number` and type of `y` is `String`
  Convert `y` to `Number`
  Return the result of the comparison `x == y`
If type of `x` is `String` and type of `y` is `Number`
  Convert `x` to `Number`
  Return the result of the comparison `x == y`

If type of `x` is `Boolean`
  Convert `x` to `Number`
  Return the result of the comparison `x == y`
If type of `y` is `Boolean`
  Convert `y` to `Number`
  Return the result of the comparison `x == y`

If type of `x` is either `String` or `Number` and type of `y` is `Object`
  Convert `y` to primitive value
  Return the result of the comparison `x == y`
If type of `x` is `Object` and type of `y` is either `String` or `Number`
  Convert `x` to primitive value
  Return the result of the comparison `x == y`

Return `false`
```

> Keep in mind that some parts of the algorithm are missing in pseudocode representation. If you want to see detailed specifications, check it out on ECMAScript website.

Yes, I know, it's just a bunch of if statements and conversions. This conversion part is really what's important. You see, when types are the same, `==` behaves exactly the same as the `===` operator. Important difference is that `==` enforces coercion when types are different.

Coercion is a fancy JavaScript term used when we talk about converting from one value type to another, like from string to number. Coercion plays an important role in `==` comparison.

If you don't want to remember the whole algorithm, think about it in this way. If operands do not have the same type, they are converted to `Number`. When types are the same, operands are compared by value.

## Object to primitive

One more important thing, before we focus on some examples, is conversion of the `Object` type values to primitive values.

> In JavaScript, there are 7 primitive values: _string_, _number_, _bigint_, _boolean_, _undefined_, _null_ and _symbol_.

Value with the type of `Object` can automatically be converted to primitive value by `==` operator. This conversion will happen when the type of one operand is `Object` while the other operand has one of primitive types.

When converting `Object` to primitive value, JavaScript will do a couple of things. It will first try to call the built-in prototype method `valueOf`. If `valueOf` does not return a primitive value, the built-in prototype method `toString` will be called next.

As almost anything in JavaScript this behavior has a couple of edge cases and it can be modified and overridden, but you don't have to worry about it for now.

## Examples

Still remember that code sample from the beginning? Let's cover it line by line, now when we know how the [algorithm works](#islooselyequal-algorithm).

I will make one assumption here. Operand on the left side of `==` will be called `x` and the operand on the right side will be called `y`. With that out of the way, let's get started.

Keep in mind, if operands do not have the same type, `==` will enforce **type conversion** as long as operands have different types. Only then will operands be compared by value.

### Sample 01

```javascript
'' == '0'             // Result: false
```

This one is pretty simple. Both operands have the same type, `String`. This means that operands will be compared by value. Since values are not the same, the result is `false`.

### Sample 02

```javascript
0 == ''               // Result: true
```

Type of operand `x` is `Number` and type of operand `y` is `String`. Types are not the same and `y` will be converted to `Number`. 

Empty string `''` converted to `Number`, will be `0`. Test it by running `Number('')` in your browser's console.

When conversion is completed operands will be compared by value. Since values are the same, `0` is indeed equal to `0`, result will be `true`.

### Sample 03

```javascript
0 == '0'              // Result: true
```

Type of operand `x` is `Number` and the type of operand `y` is `String`. Operand `y` will be converted to `Number`.

When `'0'` is converted to `Number`, its value is going to be `0`. Test it by running `Number('0')` in your browser's console.

When conversion is completed operands will be compared by value. Since values are the same, `0` is indeed equal to `0`, result will be `true`.

### Sample 04

```javascript
false == undefined    // Result: false
false == null         // Result: false
null == undefined     // Result: true
```

I grouped these examples together since the reasoning for this behavior, in all of them, is pretty much the same.

According to the algorithm, `undefined` can only be equal to `undefined` or `null`. Same is for `null`, it can only be equal to `null` or `undefined`. Because of this, comparing them to `false` will always result in `false`.

### Sample 05

```javascript
[] == 0               // Result: true
```

Operand `x` is the type of `Object` and the type of operand `y` is `String`. As we already know, before they can be compared by value, type conversion is going to happen. In this case, operand `x` will be converted to a primitive value. This means [`Object` to primitive](#object-to-primitive) conversion will kick in.

So, let's see how the conversion of `[]` to primitive value looks like. Method `valueOf` is going to be called on `[]` directly. Test it by running `[].valueOf()` in your browser's console. The result of this operation will be `[]`.

As you can see, we still have `[]` which is not a primitive value, so method `toString()` gets called. Test it by running `[].toString()` in your browser's console. Returned value is `''` which is a primitive value. With this, conversion of `Object` to primitive value is finally completed.

But, look closely, operands still have different types, `x` is now `''` which is type `String` and `y` is still `0` which is type `Number`. Conversion must continue.

Now, `x` will be converted to `Number`. We already know, from previous examples, that `''` is going to be `0` after conversion. When conversion is completed operands will be compared by value. Since values are the same, `0` is indeed equal to `0`, result will be `true`.

### Sample 06

```javascript
[] == '0'             // Result: false
```

Operand `x` is type of `Object`, but operand `y` is type of `String`. Operand `x`, with value `[]`, will be converted to primitive value. We already know, from [Sample 05](#sample-05), the result of this conversion is going to be empty string `''`. After conversion, operand `x` will have value `''` and type `String`.

Since both operands are now the same type, `String`, conversion is completed. When compared by value, `''` is not the same as `'0'`, so this comparison is `false`.

### Sample 07

```javascript
['a', 'b'] == 'a,b'   // Result: true
```

Operand `x` is type of `Object` and operand `y` is type of `String`. In the conversion step, `Object` will be converted [to primitive](#object-to-primitive) value. 

After conversion, operand `x` will have value `'a,b'` and type `String`. Test it by running `['a', 'b'].toString()` in your browser's console.

Both operands are now types of `String`. When compared by value, the result is `true`, since values are the same.

### Sample 08

```javascript
NaN == NaN            // Result: false
```

This one is quite strange and yet simple. Comparing any value with `NaN` is always going to result in `false`. This is the case even if `NaN` is compared to `NaN`, which seems quite weird.

### Sample 09

```javascript
'\n\r\t' == 0         // Result: true
```

Operands have different types, so conversion is required. Since the type of `x` is `String` it will be converted to `Number`. After conversion, `'\n\r\t'` will be `0`. Now, when compared by values, `0` is the same as `0`, so the result is `true`.

If you are wondering how `'\n\r\t'`, after conversion, can become `0` - it's because of how conversion to `Number` works.

There are some special [Single Character Escape Sequences](https://262.ecma-international.org/#table-string-single-character-escape-sequences) like `\n` (new line), `\t` (horizontal tab), `\v` (vertical tab), `\r` (carriage return) that are always going to be `0` when converted to `Number`. It does not matter in which order and how many of them are contained inside the same string value.

## Conclusion

We all know JavaScript can be weird, but if you look a bit under the hood, things can become much clearer. The same thing is with the Equality `==` operator. On the surface it can be really strange, but when you know the algorithm behind it, things can become much easier to understand.

Either way, I strongly recommend **always using** the Strict Equality `===` operator. But don't get me wrong, it is still really important to know how `==` works. It is, after all, a JavaScript feature which is always going to be part of the language.

---

Check out other articles on [my blog](https://ramomujagic.com/blog/).