---
title: 'Equality Comparisons'
source: 'https://academia.sh/en/courses/javascript-fundamentals/equality-comparisons'
course: 'JavaScript Fundamentals'
language: en
updated: '2026-08-23T07:00:58+00:00'
license: 'CC BY-SA 4.0'
---

# Equality Comparisons

Strict equality, the step-by-step algorithm of loose equality, same-value equality, and identity comparison for objects.

The previous lesson defined the coercion procedures: how a value is converted to number,
to string, and to a primitive value. This lesson takes up the place those procedures are
used the most. JavaScript has three separate ways to compare two values, and the three
answer different questions.

All three diverge on a single axis: whether coercion is applied before comparison, and
how the special number values (`NaN`, `-0`) are handled. Knowing the distinction turns
the question "why did this come out equal" from a guess into a traceable step.

## Strict Equality

The `===` operator **looks at type first**. If the types differ, the result is directly
false; no coercion is done. If the types are the same, the values are compared.

```js
console.log(21.5 === 21.5);
console.log("A1" === "A1");
console.log(1 === "1");
console.log(NaN === NaN);
console.log(0 === -0);
```

```
true
true
false
false
true
```

The third line gives false because of the type difference. The last two lines are two
exceptions specific to the number type, and both come from the IEEE 754 representation.
`NaN` is not equal to any value, including itself; the standard writes this explicitly.
Signed zeros, despite being different bit patterns, are considered equal.

`NaN` not being equal to itself makes it impossible to test whether a value is `NaN`
using equality. There is a separate probe for this:

```js
console.log(Number.isNaN(NaN));
console.log(Number.isNaN("abc"));
console.log(isNaN("abc"));
```

```
true
false
true
```

The difference between the two probes matters. `Number.isNaN` requires the value to
**be of number type and be `NaN`**. The general probe first converts its argument to a
number; because the text `"abc"` converts to `NaN`, it gives true. The second answers the
question "can this value not be converted to a number," the first answers "is this value
`NaN`." Only the first will be used in this course.

## Loose Equality

The `==` operator, if the types differ, applies coercion and descends to a common type.
Its steps are defined in sequence in the standard, and in practice the following five
rules are enough:

1. If the types are the same, strict equality is applied.
2. If one side is `null` and the other is `undefined`, the result is true. Neither of
   these is loosely equal to any other value.
3. If one side is a number and the other is a string, the string is converted to a
   number.
4. If one side is a boolean, **that side is converted to a number first**, then the rules
   are applied again.
5. If one side is an object and the other is primitive, the object is converted to a
   primitive value and the rules are applied again.

Results of the rules:

```js
console.log(null == undefined);
console.log(null == 0, undefined == 0);
console.log("1" == 1);
console.log("" == 0);
console.log("0" == 0);
console.log("" == "0");
```

```
true
false false
true
true
true
false
```

The second line is a direct consequence of the second rule: `null` is not converted to a
number; it only matches `undefined`. This is the one behavior that makes it safe to write
a `null` check with loose equality.

The boolean rule produces the most counterintuitive results:

```js
console.log(true == 1, true == "1", false == "");
console.log([] == false);
console.log([21.5] == 21.5);
console.log([1, 2] == "1,2");
```

```
true true true
true
true
true
```

Reading the fourth line step by step shows the method. In the expression `[] == false`,
the fourth rule applies first: `false` is converted to a number, becoming `0`. Now one
side is an object, the other a number; per the fifth rule the array is converted to a
primitive value and becomes an empty string. In the last step, the third rule converts
the empty string to a number; the result becomes `0 == 0` and comes out true. No step is
arbitrary.

## Loose Equality Is Not Transitive

One of the properties expected of an equality relation is transitivity: if $a = b$ and
$b = c$, then $a = c$ should hold. Loose equality does not carry this property:

```js
console.log("0" == 0);
console.log(0 == "");
console.log("0" == "");
```

```
true
true
false
```

In the first two lines, the comparison was done through number; in the third line,
because both sides are strings, the first rule kicks in and there is no coercion. The
result is a relation that does not contradict itself but is not transitive.

The course's rule follows from this:

> **Equality comparisons are written with `===`.** The one exception is the `value ==
> null` form, which tests together whether a value is `null` or `undefined`.

The reasoning for the exception is the second rule: this form covers exactly two values,
and accidentally includes no other value.

```js
const incoming = { station: "A2", humidity: undefined };
console.log("loose:", incoming.humidity == null);
console.log("strict:", incoming.humidity === null);
```

```
loose: true
strict: false
```

The two lines answer different questions. The first is "does this field have no value at
all"; the second is "is this field deliberately empty." The distinction in the Primitive
Types lesson determines which question needs to be asked.

## Same-Value Equality

The third way removes both of strict equality's exceptions:

```js
console.log(Object.is(NaN, NaN));
console.log(Object.is(0, -0));
console.log(Object.is(21.5, 21.5));
```

```
true
false
true
```

`Object.is` asks whether two values are **the same value**. `NaN` is the same value as
itself; `0` and `-0` are different values. This is the relation used internally in
several places in the standard.

`Object.is` is not needed in everyday use; `===` together with `Number.isNaN` does the
same job and is more readable. Knowing the distinction is necessary to explain the
behavior of built-in methods — for example, some array search methods can find `NaN`
while others cannot, because they use different relations.

## Comparing Objects

None of the three operators compares object content. What is compared for objects is
**identity**: whether two references point to the same object.

```js
const a = { station: "A1" };
const b = { station: "A1" };
const c = a;
console.log(a === b);
console.log(a === c);
console.log(a == b);
```

```
false
true
false
```

`a` and `b` have the same content but are different objects. `c` is equal because it
points to the same object as `a`. Loose equality gives the same result: because both
sides are objects, the first rule kicks in and no coercion happens.

The identity–equality distinction established in the Variables and Binding lesson of the
Programming Fundamentals course maps directly onto the language's operators here. If
content comparison is needed, it has to be written; the language does not provide it.

## Application to the Measurement Script

Checks that test records' validity, when written with the right operator, distinguish
both missing and corrupted fields:

```js
const records = [
  { station: "A1", temperature: 21.5, humidity: null },
  { station: "A2", temperature: 0, humidity: 55 },
  { station: "B1", temperature: NaN, humidity: 41 },
];

for (const record of records) {
  const humidityMissing = record.humidity == null;
  const temperatureInvalid = Number.isNaN(record.temperature);
  console.log(record.station, "humidity missing:", humidityMissing, "temperature invalid:", temperatureInvalid);
}
```

```
A1 humidity missing: true temperature invalid: false
A2 humidity missing: false temperature invalid: false
B1 humidity missing: false temperature invalid: true
```

The `0` value in the second record passed both checks — zero is neither missing nor
invalid. This is exactly the situation the shorthand form broke in the previous lesson.
The `NaN` in the third record can only be caught with `Number.isNaN`; the form
`record.temperature === NaN` would never give true.

## Summary

- `===` looks at type first; if the types differ, it gives false without coercion.
- `NaN` is not equal to any value, not even itself; `0` and `-0` are equal under strict
  equality.
- `==` descends to a common type with five rules; `null` and `undefined` are equal only
  to each other, booleans are converted to number first, objects are reduced to a
  primitive value.
- Loose equality is not transitive; this is why comparisons are written with `===`, with
  the one exception being the `value == null` form.
- `Object.is` is same-value equality: `NaN` equals itself, `0` and `-0` differ.
- Objects are compared by identity; content comparison does not happen unless written.

## Next Step

The question left from the previous lesson is still open: what happens when a number,
string, or object is placed inside a condition? The form `if (value)` is not an equality
comparison; it calls the third coercion procedure, the one that converts a value to
boolean. The next lesson will give this procedure's full list and show why the value `0`
is mistaken for missing data.
