Why does 1 == "1" return true but 1 === "1" return false? Why does typeof null return "object"? And why is NaN the only value in JavaScript that isn't equal to itself?
// Same values, different results
console.log(1 == "1"); // true — loose equality converts types
console.log(1 === "1"); // false — strict equality checks type first
// The famous quirks
console.log(typeof null); // "object" — a bug from 1995!
console.log(NaN === NaN); // false — NaN never equals anythingUnderstanding JavaScript's equality operators is crucial because comparison bugs are among the most common in JavaScript code. According to the ECMAScript specification (ECMA-262), JavaScript defines three distinct equality algorithms: Abstract Equality, Strict Equality, and SameValue. This guide will teach you exactly how ==, ===, and Object.is() work, and when to use each one.
What you'll learn in this guide:
- The difference between
==(loose) and===(strict) equality - How JavaScript converts values during loose equality comparisons
- The
typeofoperator and its famous quirks (including thenullbug) - When to use
Object.is()for edge cases likeNaNand-0 - Common comparison mistakes and how to avoid them
- A simple rule: when to use which operator
Prerequisites: This guide assumes you understand Primitive Types and Type Coercion. Equality operators rely heavily on how JavaScript converts types. If those concepts are new to you, read those guides first!
The Three Equality Operators: Overview
JavaScript provides three ways to compare values for equality. Here's the quick summary:
| Operator | Name | Type Coercion | Best For |
|---|---|---|---|
== | Loose (Abstract) Equality | Yes | Checking null/undefined only |
=== | Strict Equality | No | Default choice for everything |
Object.is() | Same-Value Equality | No | Edge cases (NaN, ±0) |
// The same comparison, three different results
const num = 1;
const str = "1";
console.log(num == str); // true (coerces string to number)
console.log(num === str); // false (different types)
console.log(Object.is(num, str)); // false (different types)The simple rule: Always use === for comparisons. The only exception: use == null to check if a value is empty (null or undefined). You'll rarely need Object.is(). It's for special cases we'll cover later.
The Teacher Grading Papers: A Real-World Analogy
Imagine a teacher grading a math test. The question asks: "What is 2 + 2?"
One student writes: 4
Another student writes: "4" (as text)
A third student writes: 4.0
How strict should the teacher be when grading?
RELAXED GRADING (==) STRICT GRADING (===)
"Is the answer correct?" "Is it exactly right?"
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 4 │ = │ "4" │ │ 4 │ ≠ │ "4" │
│ (number) │ │ (string) │ │ (number) │ │ (string) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
└────────┬────────┘ └────────┬────────┘
▼ ▼
"Close enough!" ✓ "Different types!" ✗JavaScript gives you both types of teachers:
- Loose equality (
==) — The relaxed teacher. Accepts4and"4"as the same answer because the meaning is similar. Converts values to match before comparing. - Strict equality (
===) — The strict teacher. Only accepts the exact answer in the exact format. The number4and the string"4"are different answers. typeof— Asks "What kind of answer is this?" Is it a number? A string? Something else?Object.is()— The most precise teacher. Even stricter than===— can spot tiny differences that others miss.
TL;DR: Use === for almost everything. Use == null to check for both null and undefined. Use Object.is() only for NaN or -0 edge cases.
Loose Equality (==): The Relaxed Comparison
The == operator tries to be helpful. Before comparing two values, it converts them to the same type. This automatic conversion is called type coercion.
For example, if you compare the number 5 with the string "5", JavaScript thinks: "These look similar. Let me convert them and check." So 5 == "5" returns true.
How It Works
When you write x == y, JavaScript asks:
- Are
xandythe same type? → Compare them directly - Are they different types? → Convert one or both to match, then compare
This automatic conversion can be helpful, but it can also cause unexpected results.
The Abstract Equality Comparison Algorithm
Here's the complete algorithm from the ECMAScript specification. When comparing x == y:
Same Type?
If x and y are the same type, perform strict equality comparison (===).
5 == 5 // Same type (number), compare directly → true
"hello" == "hello" // Same type (string), compare directly → truenull and undefined
If x is null and y is undefined (or vice versa), return true.
null == undefined // true (special case!)
undefined == null // trueNumber and String
If one is a Number and the other is a String, convert the String to a Number.
5 == "5" // "5" → 5, then 5 == 5 → true
0 == "" // "" → 0, then 0 == 0 → true
42 == "42" // "42" → 42, then 42 == 42 → trueBigInt and String
If one is a BigInt and the other is a String, convert the String to a BigInt.
10n == "10" // "10" → 10n, then 10n == 10n → trueBoolean Conversion
If either value is a Boolean, convert it to a Number (true → 1, false → 0).
true == 1 // true → 1, then 1 == 1 → true
false == 0 // false → 0, then 0 == 0 → true
true == "1" // true → 1, then 1 == "1" → 1 == 1 → trueObject to Primitive
If one is an Object and the other is a String, Number, BigInt, or Symbol, convert the Object to a primitive using ToPrimitive.
[1] == 1 // [1] → "1" → 1, then 1 == 1 → true
[""] == 0 // [""] → "" → 0, then 0 == 0 → trueBigInt and Number
If one is a BigInt and the other is a Number, compare their mathematical values.
10n == 10 // Compare values: 10 == 10 → true
10n == 10.5 // 10 !== 10.5 → falseNo Match
If none of the above rules apply, return false.
null == 0 // false (null only equals undefined)
undefined == 0 // false
Symbol() == Symbol() // false (Symbols are always unique)Visual: The Coercion Decision Tree
x == y
│
┌────────────┴────────────┐
▼ ▼
Same type? Different types?
│ │
YES YES
│ │
▼ ▼
Compare values ┌────────┴────────┐
(like ===) │ │
▼ ▼
null == undefined? Apply coercion
│ rules above
YES │
│ ▼
▼ Convert types
true then compare
againThe Complete Coercion Rules Table
| Type of x | Type of y | Coercion Applied |
|---|---|---|
| Number | String | ToNumber(y) — String becomes Number |
| String | Number | ToNumber(x) — String becomes Number |
| BigInt | String | ToBigInt(y) — String becomes BigInt |
| String | BigInt | ToBigInt(x) — String becomes BigInt |
| Boolean | Any | ToNumber(x) — Boolean becomes Number (0 or 1) |
| Any | Boolean | ToNumber(y) — Boolean becomes Number (0 or 1) |
| Object | String/Number/BigInt/Symbol | ToPrimitive(x) — Object becomes primitive |
| String/Number/BigInt/Symbol | Object | ToPrimitive(y) — Object becomes primitive |
| BigInt | Number | Compare mathematical values directly |
| Number | BigInt | Compare mathematical values directly |
| null | undefined | true (special case) |
| undefined | null | true (special case) |
| null | Any (except undefined) | false |
| undefined | Any (except null) | false |
Surprising Results Gallery
Here are some comparison results that surprise most developers. Understanding why these happen will help you avoid bugs in your code:
// String converted to Number
1 == "1" // true ("1" → 1)
0 == "" // true ("" → 0)
0 == "0" // true ("0" → 0)
100 == "1e2" // true ("1e2" → 100)
// But string-to-string is direct comparison
"" == "0" // false (both strings, different values)
// NaN conversions (NaN is "Not a Number")
NaN == "NaN" // false (NaN ≠ anything, including itself)
0 == "hello" // false ("hello" → NaN, 0 ≠ NaN)// Booleans become 0 or 1 FIRST
true == 1 // true (true → 1)
false == 0 // true (false → 0)
true == "1" // true (true → 1, "1" → 1)
false == "" // true (false → 0, "" → 0)
// This is why these are confusing:
true == "true" // false! (true → 1, "true" → NaN)
false == "false" // false! (false → 0, "false" → NaN)
// And these seem wrong:
true == 2 // false (true → 1, 1 ≠ 2)
true == "2" // false (true → 1, "2" → 2, 1 ≠ 2)Common trap: true == "true" is false! The boolean true becomes 1, and the string "true" becomes NaN. Since 1 ≠ NaN, the result is false.
// The special relationship
null == undefined // true (special rule!)
undefined == null // true
// But they don't equal anything else
null == 0 // false
null == false // false
null == "" // false
undefined == 0 // false
undefined == false // false
undefined == "" // false
// This is actually useful!
let value = null;
if (value == null) {
// Catches both null AND undefined
console.log("Value is nullish");
}This is the ONE legitimate use case for ==. Using value == null checks for both null and undefined in a single comparison.
// Arrays convert via ToPrimitive (usually toString)
[] == false // true ([] → "" → 0, false → 0)
[] == 0 // true ([] → "" → 0)
[] == "" // true ([] → "")
[1] == 1 // true ([1] → "1" → 1)
[1] == "1" // true ([1] → "1")
[1,2] == "1,2" // true ([1,2] → "1,2")
// Empty array gotcha
[] == ![] // true! (see explanation below)
// Objects with valueOf/toString
let obj = { valueOf: () => 42 };
obj == 42 // true (obj.valueOf() → 42)Step-by-Step Trace: [] == ![]
This is one of JavaScript's most surprising results. An empty array [] equals ![]? Let's break down why this happens step by step:
Evaluate ![]
First, JavaScript evaluates ![].
[]is truthy (all objects are truthy)![]therefore equalsfalse- Now we have:
[] == false
Boolean to Number
One side is a Boolean, so convert it to a Number.
false→0- Now we have:
[] == 0
Object to Primitive
One side is an Object, so convert it via ToPrimitive.
[]→""(empty array's toString returns empty string)- Now we have:
"" == 0
String to Number
One side is a String and one is a Number, so convert the String.
""→0(empty string becomes 0)- Now we have:
0 == 0
Final Comparison
Both sides are Numbers with the same value.
0 == 0→true
// The chain of conversions:
[] == ![]
[] == false // ![] → false
[] == 0 // false → 0
"" == 0 // [] → ""
0 == 0 // "" → 0
true // 0 equals 0!This example shows why == can produce unexpected results. An empty array appears to equal its own negation! This isn't a bug. It's how JavaScript's conversion rules work. This is why most developers prefer ===.
When == Might Be Useful
Despite its quirks, there's one legitimate use case for loose equality:
// Checking for null OR undefined in one comparison
function greet(name) {
// Using == (the one acceptable use case!)
if (name == null) {
return "Hello, stranger!";
}
return `Hello, ${name}!`;
}
// Both null and undefined are caught
greet(null); // "Hello, stranger!"
greet(undefined); // "Hello, stranger!"
greet("Alice"); // "Hello, Alice!"
greet(""); // "Hello, !" (empty string is NOT null)
greet(0); // "Hello, 0!" (0 is NOT null)This is equivalent to the more verbose:
function greet(name) {
if (name === null || name === undefined) {
return "Hello, stranger!";
}
return `Hello, ${name}!`;
}Many style guides (including those from Airbnb and StandardJS) make an exception for value == null because it's a clean way to check for "nullish" values. However, you can also use the nullish coalescing operator (??) or optional chaining (?.) introduced in ES2020.
Strict Equality (===): The Reliable Choice
The strict equality operator compares two values without any conversion. If the types are different, it immediately returns false.
This is the operator you should use almost always. It's simple and predictable: the number 1 and the string "1" are different types, so 1 === "1" returns false. No surprises.
How It Works
When you write x === y, JavaScript asks:
- Are
xandythe same type? No → returnfalse - Same type? → Compare their values
That's it. No conversions, no surprises (well, almost. There's one special case with NaN).
The Strict Equality Comparison Algorithm
Type Check
If x and y are different types, return false immediately.
1 === "1" // false (number vs string)
true === 1 // false (boolean vs number)
null === undefined // false (null vs undefined)Number Comparison
If both are Numbers:
- If either is
NaN, returnfalse - If both are the same numeric value, return
true +0and-0are considered equal
42 === 42 // true
NaN === NaN // false (!)
+0 === -0 // true
Infinity === Infinity // trueString Comparison
If both are Strings, return true if they have the same characters in the same order.
"hello" === "hello" // true
"hello" === "Hello" // false (case sensitive)
"hello" === "hello " // false (different length)Boolean Comparison
If both are Booleans, return true if they're both true or both false.
true === true // true
false === false // true
true === false // falseBigInt Comparison
If both are BigInts, return true if they have the same mathematical value.
10n === 10n // true
10n === 20n // falseSymbol Comparison
If both are Symbols, return true only if they are the exact same Symbol.
const sym = Symbol("id");
sym === sym // true
Symbol("id") === Symbol("id") // false (different symbols!)Object Comparison (Reference)
If both are Objects (including Arrays and Functions), return true only if they are the same object (same reference in memory).
const obj = { a: 1 };
obj === obj // true (same reference)
{ a: 1 } === { a: 1 } // false (different objects!)
[] === [] // false (different arrays!)null and undefined
null === null returns true. undefined === undefined returns true. But null === undefined returns false (different types).
null === null // true
undefined === undefined // true
null === undefined // falseVisual: Strict Equality Flowchart
x === y
│
┌───────────────┴───────────────┐
│ Same type? │
└───────────────┬───────────────┘
│ │
NO YES
│ │
▼ ▼
false Both NaN?
│
┌───────┴───────┐
YES NO
│ │
▼ ▼
false Same value?
(NaN never equals │
anything!) ┌───────┴───────┐
YES NO
│ │
▼ ▼
true falseThe Predictable Results
With ===, what you see is what you get:
// All of these are false (different types)
1 === "1" // false
0 === "" // false
true === 1 // false
false === 0 // false
null === undefined // false
[] === "" // false
// All of these are true (same type, same value)
1 === 1 // true
"hello" === "hello" // true
true === true // true
null === null // true
undefined === undefined // trueSpecial Cases: Two Exceptions to Know
Even === has two edge cases that might surprise you:
// NaN is the only value that is not equal to itself
NaN === NaN // false!
// NaN doesn't equal anything, not even itself!
// This is part of how numbers work in all programming languages
// This is by design (IEEE 754 specification)
// NaN represents "Not a Number" - an undefined result
// Since it's not a specific number, it can't equal anything
// How to check for NaN:
Number.isNaN(NaN) // true (recommended)
isNaN(NaN) // true (but has quirks — see below)
Object.is(NaN, NaN) // true (ES6)
// The isNaN() quirk:
isNaN("hello") // true! (converts to NaN first)
Number.isNaN("hello") // false (no conversion)Always use Number.isNaN() instead of the global isNaN(). The global isNaN() function converts its argument to a Number first, which means isNaN("hello") returns true. That's rarely what you want.
// Positive zero and negative zero are considered equal
+0 === -0 // true
-0 === 0 // true
// But they ARE different! Watch this:
1 / +0 // Infinity
1 / -0 // -Infinity
// Two zeros, two different infinities. Math is wild.
// How to distinguish them:
Object.is(+0, -0) // false (ES6)
1 / +0 === 1 / -0 // false (Infinity vs -Infinity)
// When does -0 appear?
0 * -1 // -0
Math.sign(-0) // -0
JSON.parse("-0") // -0You'll rarely need to tell +0 and -0 apart unless you're doing advanced math or physics calculations.
Object Comparison: Reference vs Value
This is one of the most important concepts to understand:
// Objects are compared by REFERENCE, not by value
const obj1 = { name: "Alice" };
const obj2 = { name: "Alice" };
const obj3 = obj1;
obj1 === obj2 // false (different objects in memory)
obj1 === obj3 // true (same reference)
// Same with arrays
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
const arr3 = arr1;
arr1 === arr2 // false (different arrays)
arr1 === arr3 // true (same reference)
// And functions
const fn1 = () => {};
const fn2 = () => {};
const fn3 = fn1;
fn1 === fn2 // false (different functions)
fn1 === fn3 // true (same reference)MEMORY VISUALIZATION:
obj1 ──────┐
├──► { name: "Alice" } (Object A)
obj3 ──────┘
obj2 ──────────► { name: "Alice" } (Object B)
obj1 === obj3 → true (both point to Object A)
obj1 === obj2 → false (different objects, even with same content)To compare objects by their content (deep equality), you need to:
- Use
JSON.stringify()for simple objects (has limitations) - Write a recursive comparison function
- Use a library like Lodash's
_.isEqual()
Object.is(): Same-Value Equality
ES6 introduced Object.is() to fix the two edge cases where === gives unexpected results. As documented by MDN, Object.is() implements the "SameValue" algorithm from the ECMAScript specification. It works exactly like ===, but handles NaN and -0 correctly.
Why It Exists
// The two cases where === is "wrong"
NaN === NaN // false (but NaN IS NaN!)
+0 === -0 // true (but they ARE different!)
// Object.is() fixes both
Object.is(NaN, NaN) // true ✓
Object.is(+0, -0) // false ✓How It Differs from ===
Object.is() behaves exactly like === except for these two cases:
| Expression | === | Object.is() |
|---|---|---|
NaN, NaN | false | true |
+0, -0 | true | false |
-0, 0 | true | false |
1, 1 | true | true |
"a", "a" | true | true |
null, null | true | true |
{}, {} | false | false |
Complete Comparison Table
| Values | == | === | Object.is() |
|---|---|---|---|
1, "1" | true | false | false |
0, false | true | false | false |
null, undefined | true | false | false |
NaN, NaN | false | false | true |
+0, -0 | true | true | false |
[], [] | false | false | false |
{}, {} | false | false | false |
When to Use Object.is()
// 1. Checking for NaN (alternative to Number.isNaN)
function isReallyNaN(value) {
return Object.is(value, NaN);
}
// 2. Distinguishing +0 from -0 (rare, but needed in math/physics)
function isNegativeZero(value) {
return Object.is(value, -0);
}
// 3. Implementing SameValue comparison (like in Map/Set)
// Maps use SameValueZero (like Object.is but +0 === -0)
const map = new Map();
map.set(NaN, "value");
map.get(NaN); // "value" (NaN works as a key!)
// 4. Library code and polyfills
// When you need exact specification complianceFor most everyday code, you won't need Object.is(). Use === as your default, and reach for Object.is() only when you specifically need to handle NaN or ±0 edge cases.
The typeof Operator
The typeof operator tells you what type a value is. It returns a string like "number", "string", or "boolean". The 2023 State of JS survey found that TypeScript adoption continues to grow — partly driven by developers seeking to avoid the type-checking pitfalls that typeof alone cannot solve. It's very useful, but it has some famous quirks that surprise many developers.
How It Works
typeof operand
typeof(operand) // Both forms are validComplete Results Table
| Value | typeof Result | Notes |
|---|---|---|
"hello" | "string" | |
42 | "number" | Includes Infinity, NaN |
42n | "bigint" | ES2020 |
true / false | "boolean" | |
undefined | "undefined" | |
Symbol() | "symbol" | ES6 |
null | "object" | Famous bug! |
{} | "object" | |
[] | "object" | Arrays are objects |
function(){} | "function" | Special case |
class {} | "function" | Classes are functions |
new Date() | "object" | |
/regex/ | "object" |
The Famous Quirks
Better Alternatives for Type Checking
Since typeof has limitations, here are more reliable approaches:
// Arrays
Array.isArray(value) // true for arrays only
// NaN
Number.isNaN(value) // true for NaN only (no coercion)
// Finite numbers
Number.isFinite(value) // true for finite numbers
// Integers
Number.isInteger(value) // true for integers
// Safe integers
Number.isSafeInteger(value) // true for safe integersThese methods from Number are more reliable than typeof for numeric checks.
The instanceof operator checks if an object is an instance of a constructor:
// Check if an object is an instance of a constructor
[] instanceof Array // true
{} instanceof Object // true
new Date() instanceof Date // true
/regex/ instanceof RegExp // true
// Works with custom classes
class Person {}
const p = new Person();
p instanceof Person // true
// Caveat: doesn't work across iframes/realms
// The Array in iframe A is different from Array in iframe BThe Object.prototype.toString method is the most reliable for getting precise type information:
const getType = (value) =>
Object.prototype.toString.call(value).slice(8, -1);
getType(null) // "Null"
getType(undefined) // "Undefined"
getType([]) // "Array"
getType({}) // "Object"
getType(new Date()) // "Date"
getType(/regex/) // "RegExp"
getType(new Map()) // "Map"
getType(new Set()) // "Set"
getType(Promise.resolve()) // "Promise"
getType(function(){}) // "Function"
getType(42) // "Number"
getType("hello") // "String"
getType(Symbol()) // "Symbol"
getType(42n) // "BigInt"A comprehensive type-checking utility:
function getType(value) {
// Handle null specially (typeof bug)
if (value === null) return "null";
// Handle primitives
const type = typeof value;
if (type !== "object" && type !== "function") {
return type;
}
// Handle objects with Object.prototype.toString
const tag = Object.prototype.toString.call(value);
return tag.slice(8, -1).toLowerCase();
}
// Usage
getType(null) // "null"
getType([]) // "array"
getType({}) // "object"
getType(new Date()) // "date"
getType(/regex/) // "regexp"
getType(new Map()) // "map"
getType(Promise.resolve()) // "promise"Decision Guide: Which to Use?
The Simple Rule
Default to === for all comparisons. It's predictable, doesn't perform type coercion, and will save you from countless bugs.
The only exception: Use == null to check for both null and undefined in one comparison.
Decision Flowchart
Need to compare two values?
│
▼
┌───────────────────────────────┐
│ Checking for null/undefined? │
└───────────────────────────────┘
│ │
YES NO
│ │
▼ ▼
┌──────────┐ ┌───────────────────┐
│ == null │ │ Need NaN or ±0? │
└──────────┘ └───────────────────┘
│ │
YES NO
│ │
▼ ▼
┌──────────┐ ┌─────────┐
│Object.is │ │ === │
│ or │ └─────────┘
│Number. │
│ isNaN() │
└──────────┘Quick Reference
| Scenario | Use | Example |
|---|---|---|
| Default comparison | === | if (x === 5) |
| Check nullish | == null | if (value == null) |
| Check NaN | Number.isNaN() | if (Number.isNaN(x)) |
| Check array | Array.isArray() | if (Array.isArray(x)) |
| Check type | typeof | if (typeof x === "string") |
| Distinguish ±0 | Object.is() | Object.is(x, -0) |
ESLint Configuration
Most style guides enforce === with an exception for null checks:
// .eslintrc.js
module.exports = {
rules: {
// Require === and !== except for null comparisons
"eqeqeq": ["error", "always", { "null": "ignore" }]
}
};This allows:
// Allowed
if (value === 5) { } // Using ===
if (value == null) { } // Exception for null
// Error
if (value == 5) { } // Should use ===Common Gotchas and Mistakes
These common mistakes trip up many JavaScript developers. Learning about them now will save you debugging time later:
Common Misconceptions
Key Takeaways
The key things to remember about Equality Operators:
-
Use
===by default — It's predictable and doesn't convert types -
==converts types first — This leads to unexpected results like"0" == falsebeingtrue -
Only use
==for null checks —value == nullchecks for bothnullandundefined -
NaN !== NaN— NaN doesn't equal anything, not even itself. UseNumber.isNaN()to check for it -
Objects compare by reference —
{} === {}isfalsebecause they're different objects in memory -
typeof null === "object"— This is a bug that can't be fixed. Always check fornulldirectly -
Object.is()for edge cases — Use it when you need to check forNaNor distinguish+0from-0 -
Arrays return
"object"from typeof — UseArray.isArray()to check for arrays -
These rules are commonly asked in interviews — Now you're prepared!
-
Configure ESLint — Use the
eqeqeqrule to enforce===in your projects
Interactive Visualization Tool
The best way to internalize JavaScript's equality rules is to see all the comparisons at once.
JavaScript Equality Table
Interactive comparison table by dorey showing the results of == and === for all type combinations. Hover over cells to see explanations. An essential reference for understanding JavaScript equality!
Try these in the table:
- Compare
[]withfalse,0,"", and![]to see why[] == ![]istrue - See why
null == undefinedistruebut neither equals0orfalse - Observe how
NaNnever equals anything (including itself) - Notice how objects only equal themselves (same reference)
Bookmark this table! It's invaluable for debugging comparison issues and preparing for technical interviews.
Test Your Knowledge
Try to answer each question before revealing the solution:
Frequently Asked Questions
Related Concepts
Type Coercion
Deep dive into how JavaScript converts between types automatically
Primitive Types
Understanding JavaScript's fundamental data types
Primitives vs Objects
How primitives and objects behave differently in JavaScript
Scope and Closures
Understanding where variables are accessible in your code
Reference
Equality comparisons and sameness — MDN
Comprehensive official documentation covering ==, ===, Object.is(), and SameValue
typeof — MDN
Official documentation on the typeof operator and its behavior
Articles
JavaScript Double Equals vs. Triple Equals — Brandon Morelli
Uses side-by-side code comparisons to show exactly when == and === produce different results. Great starting point if you're new to JavaScript equality.
What is the difference between == and === in JavaScript? — Craig Buckler
O'Reilly's take on the equality debate with a clear recommendation on which operator to default to. Includes the edge cases that trip up even experienced developers.
=== vs == Comparison in JavaScript — FreeCodeCamp
Walks through the type coercion algorithm step-by-step with dozens of examples. The boolean comparison section explains why true == "true" returns false.
Checking Types in Javascript — Toby Ho
Covers the limitations of typeof and when to use instanceof, Object.prototype.toString, or duck typing instead. Includes a reusable type-checking utility function.
How to better check data types in JavaScript — Webbjocke
Provides copy-paste utility functions for checking arrays, objects, nulls, and primitives. Explains why each approach works and when to use which method.
JavaScript Equality Table — dorey
Visual comparison table showing the results of == and === for all type combinations. Essential reference!
Videos
JavaScript "==" VS "===" — Web Dev Simplified
8-minute breakdown with on-screen code examples showing type coercion in action. Kyle's explanation of the null/undefined special case is particularly helpful.
JavaScript - The typeof operator — Java Brains
Demonstrates the typeof null bug and explains why it exists. Shows how to build a reliable type-checking function that handles all edge cases.
=== vs == in JavaScript — Hitesh Choudhary
Live coding session showing surprising equality results and debugging them in the console. Great for seeing how these operators behave in real development.
== ? === ??? ...#@^% — Shirmung Bielefeld
Conference talk diving deep into JavaScript's equality quirks and type coercion weirdness.
Books
You Don't Know JS: Types & Grammar — Kyle Simpson
The definitive deep-dive into JavaScript types, coercion, and equality. Free to read online. Essential reading for truly understanding how JavaScript handles comparisons.
Read more
Understanding Typing in JavaScript: Implicit, Explicit, Nominal, Structural, and Duck Typing
A deep dive into JavaScript’s typing approaches: implicit, explicit, nominal, structural, and duck typing. Learn what they mean, how they appear in practice, and why they matter for developers.
JavaScript Value Types vs Reference Types Explained
A clear guide to understanding the difference between value types and reference types in JavaScript, with examples, quirks, and best practices.
JavaScript Primitive Types: The Complete Guide
A deep dive into JavaScript's primitive types — what they are, how they behave, their quirks, and how to use them effectively.
