How The Abstract Equality Comparison (==) In JavaScript Works
Tags: javascript, typescript, codenewbie
Published: 2021-01-02 20:06:24

The Abstract Equality Comparison Operator, a.k.a. == in JavaScript is the so-called type-coercing equality check. It coerces types if necessary and can lead to some pretty weird results.

But we can make sense of all this, and you'll see that you can follow along.

## There is an algorithm behind it
Whenever you use the == operator, there is actually an algorithm behind it that determines the result of the comparison.
This algorithm has its place within the ECMA spec (the spec behind JavaScript) and can be found in chapter 7.2.15.

It's actually pretty lengthy and takes a lot of space, but it covers all possible inputs and provides a concise way to determine the output of any comparison. You can see an excerpt from it below which I processed a little so you can read it better.

### The Algorithm

1. If Type(`x`) is the same as Type(`y`), then
    1. If Type(`x`) is Number **OR** BigInt, then
        1. If `x` is `NaN`, return `false`.
        2. If `y` is `NaN`, return `false`.
        3. If `x` has the same value as `y`, return `true`.
        4. If `x` is `+0` and `y` is `-0`, return `true`.
        5. If `x` is `-0` and `y` is `+0`, return `true`.
        6. Return `false`. 
    2. If Type(`x`) is Undefined, return `true`.
    3. If Type(`x`) is Null, return `true`.
    4. If Type(`x`) is String, then
        1. if `x` and `y` are exactly the same sequence of characters (same length, same characters in sequence), then
            1. return `true`.
        2. return `false`.
    5. If Type(`x`) is Boolean, then
        1. If `x` is `true` and `y` is `true`, then
            1. return `true`.
        2. if `x` is `false` and `y` is `false`, then
            1. return `true`.
        3. return `false`.
    6. If Type(`x`) is Symbol, then
        1. If `x` and `y` are **both** the **same** **Symbol** **value**, then
            1. return `true`.
        2. return `false`.
    7. If `x` and `y` are the same Object value, then
        1. return `true`.
    8. return `false`.
2. If `x` is `null` and `y` is `undefined`, return `true`.
3. If `x` is `undefined` and `y` is `null`, return `true`.
4. If Type(`x`) is Number and Type(`y`) is String, return the result of the comparison x == ToNumber(y).
5. If Type(`x`) is String and Type(`y`) is Number, return the result of the comparison ToNumber(x) == y.
6. If Type(`x`) is BigInt and Type(`y`) is String, then
    1. Set `n` to StringToBigInt(y).
    2. If `n` is `NaN`, return false.
    3. Return the result of the comparison `x == n.`
7. If Type(`x`) is String and Type(`y`) is BigInt, return the result of the comparison `y == x`.
8. If Type(`x`) is Boolean, return the result of the comparison ToNumber(x) == y.
9. If Type(`y`) is Boolean, return the result of the comparison x == ToNumber(y).
10. If Type(`x`) is either String, Number, BigInt, or Symbol and Type(`y`) is Object, return the result of the comparison x == ToPrimitive(y).
11. If Type(`x`) is Object and Type(`y`) is either String, Number, BigInt, or Symbol, return the result of the comparison ToPrimitive(x) == y.
12. If Type(`x`) is BigInt and Type(`y`) is Number, **OR** if Type(`x`) is Number and Type(`y`) is BigInt, then
    1. If `x` or `y` are any of `NaN`, `+INFINITY`, or `-INFINITY`, return `false`.
    2. If the mathematical value of `x` is equal to the mathematical value of `y`, then
        1.  return `true`.
    3. return `false`.
13. Return `false`.

All of this may seem a little intimidating, but that's okay. You'll see that there is enough logic to it to be understandable. But before we go in, you first have to learn about some functions that are referenced within this algorithm.

## Supporting Functions

### Type(x)
This is not the typeof operator, but a runtime function that returns exactly the type of a value at hand.

Type(null) is actually Null, for example, and not object.

### ToNumber
This is also a runtime function. It basically works the same as calling Number(x).

### StringToBigInt
This is basically ToNumber with a few additions which we won't cover now.

### ToPrimitive
This is the runtime function to convert any complex object into a primitive. There is once again a whole algorithm to it and it goes as follows.

1. If Type(input) is Object, then
    1. If `preferredType` is not present, set `hint` to `"default"`
    2. Else If `preferredType` is hint String, set `hint` to `"string"`
    3. Else
        1. Set `hint` to `"number"`
    4. Set `exoticToPrim` to `input[Symbol.iterator]`
    5. If `exoticToPrim` is not `undefined`, then
        1. Set `result` to `exoticToPrim(hint)`
        2. If Type(`result`) is **not** Object, return `result`
        3. Throw a `TypeError` exception
    6. If `hint` is `"default"`, set `hint` to `"number"`
      1. If `hint` is `"string"`, then
        1. Set `methodNames` to `["toString", "valueOf"]`
      2. Else
        1. Set `methodNames` to `["valueOf", "toString"]`
      3. For each `name` in list `methodNames`, in order, do
        1. If `input[name]` exists (not undefined), then
          1. Set `result` to `input[name]()`
          2. If Type(`result`) is **not** Object, return `result`
      4. Throw a `TypeError` exception
2. Return `input`

## Making Sense Of It With Examples
You now have 'a lot of algorithm' at hand, but it may still be pretty difficult to actually apply that knowledge. This is where examples come in. They usually help a lot in understanding more complex things.

### Example 1
Let's start with `1 == "1"` as a light entry.

```JavaScript
1 == "1";
// =>
// Step 4 applies, x is Number and y is string.
// So it is evaluated as
1 == Number("1");
// Number("1") yields 1, so we're at
1 == 1;
// which is
true;
```

### Example 2
Let's continue our examples with `true == 0`.

```JavaScript
true == 0;
// =>
// Step 9 applies, as x is a boolean.
// It is evaluated as
Number(true) == 0;
// Number(true) yields 1, so we're at
1 == 0;
// which is
false;
```

### Example 3
Let's go one step further and use one object in our comparison, so we'll go with `"" == []`.

```JavaScript
"" == [];
// =>
// Step 10 applies, as x is a string and y is an object.
// [] needs to be converted into a primitive.
// [][Symbol.toPrimitive] is undefined so that doesn't help.
// The type hint is "default", and thus "number" is passed, according to the primitive
// conversion algorithm
// The methods now used are [valueOf, toString].
// [].valueOf() returns [] again, so once again no help.
// [].toString() yields "" which is a primitive, so the algorithm terminates successfully.
"" == "";
// according to step 1.4.1 yields
true;
```

### Example 4

Let's try `[] == ![]` now. It's one of the weirder comparisons which usually makes people raise at least an eyebrow.

```JavaScript
[] == ![];
// ![] is evaluated first and is no actual part of the comparison.
// ![] is a boolean conversion together with negation, so you could also
// read it as
[] == !Boolean([]);
// Boolean([]) yields true, as all objects do
[] == !true;
// and that negated is of course
[] == false;

// Now step 9 of the algorithm applies, as y is a boolean
[] == Number(false);
// Number(false) yields 0
[] == 0;
// And the algorithm calls itself again where this time, step 11 applies, as x is an object and y is a number

ToPrimitive([]) == 0;
// [][Symbol.toPrimitive] is undefined so that doesn't help.
// The type hint is still at "default" so it gets replaced with "number" according to step 1.6 of the primitive conversion algorithm.
// The methods used are now [valueOf, toString].
// [].valueOf() yields [] again which doesn't help.
// [].toString() however results in "", an empty string.

"" == 0;
// The algorithm is once again calling itself where this time step 5 applies, as
// x is a string and y is a number.

Number("") == 0;
// Number("") results in 0 and the algorithm calls itself once again.

0 == 0;
// This time step 1.1.3 applies, as both values have type number.
// Because both sides have the exact same value, the result can only be.
true;
```

## What's next?
You can come up with your own examples now. Simply follow the algorithms and you'll surely get the right answer every time. But you can of course ensure that you are right by simply logging the expression in your browser's dev tool and then compare your result against what your browser says is right.

With a little more practice, you'll surely always know the answer to some JavaScript quizzes in the future!

## Before you leave
If you like my content, visit me on [Twitter](https://twitter.com/oliverjumpertz), and perhaps you’ll like what you see.