Lesson 09 / 21
Equality Comparisons
Strict equality, the step-by-step algorithm of loose equality, same-value equality, and identity comparison for objects.
Contents
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.
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:
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:
- If the types are the same, strict equality is applied.
- If one side is
nulland the other isundefined, the result is true. Neither of these is loosely equal to any other value. - If one side is a number and the other is a string, the string is converted to a number.
- If one side is a boolean, that side is converted to a number first, then the rules are applied again.
- 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:
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:
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 and , then should hold. Loose equality does not carry this property:
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 thevalue == nullform, which tests together whether a value isnullorundefined.
The reasoning for the exception is the second rule: this form covers exactly two values, and accidentally includes no other value.
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:
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.
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:
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.NaNis not equal to any value, not even itself;0and-0are equal under strict equality.==descends to a common type with five rules;nullandundefinedare 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 thevalue == nullform. Object.isis same-value equality:NaNequals itself,0and-0differ.- 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.
To keep your progress and take notes, Log in
My notes
Log in to take notes.