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?
==, ===, and Object.is() work, and when to use each one.
- 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
The Three Equality Operators: Overview
JavaScript provides three ways to compare values for equality. Here’s the quick summary:=== 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:4Another student writes:
"4" (as text)A third student writes:
4.0
How strict should the teacher be when grading?
- 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.
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 writex == y, JavaScript asks:
- Are
xandythe same type? → Compare them directly - Are they different types? → Convert one or both to match, then compare
The Abstract Equality Comparison Algorithm
Here’s the complete algorithm from the ECMAScript specification. When comparingx == y:
Same Type?
x and y are the same type, perform strict equality comparison (===).Number and String
BigInt and String
Boolean Conversion
true → 1, false → 0).Object to Primitive
ToPrimitive.BigInt and Number
No Match
false.Visual: The Coercion Decision Tree
The Complete Coercion Rules Table
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 & Number
- Boolean Coercion
- null & undefined
- Arrays & Objects
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 ![]
![].[]is truthy (all objects are truthy)![]therefore equalsfalse- Now we have:
[] == false
Boolean to Number
false→0- Now we have:
[] == 0
Object to Primitive
[]→""(empty array’s toString returns empty string)- Now we have:
"" == 0
String to Number
""→0(empty string becomes 0)- Now we have:
0 == 0
Final Comparison
0 == 0→true
When == Might Be Useful
Despite its quirks, there’s one legitimate use case for loose equality:
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 writex === y, JavaScript asks:
- Are
xandythe same type? No → returnfalse - Same type? → Compare their values
NaN).
The Strict Equality Comparison Algorithm
Type Check
x and y are different types, return false immediately.Number Comparison
- If either is
NaN, returnfalse - If both are the same numeric value, return
true +0and-0are considered equal
String Comparison
true if they have the same characters in the same order.Boolean Comparison
true if they’re both true or both false.BigInt Comparison
true if they have the same mathematical value.Symbol Comparison
true only if they are the exact same Symbol.Object Comparison (Reference)
true only if they are the same object (same reference in memory).null and undefined
null === null returns true. undefined === undefined returns true. But null === undefined returns false (different types).Visual: Strict Equality Flowchart
The Predictable Results
With===, what you see is what you get:
Special Cases: Two Exceptions to Know
Even=== has two edge cases that might surprise you:
- NaN !== NaN
- +0 === -0
Object Comparison: Reference vs Value
This is one of the most important concepts to understand: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
How It Differs from ===
Object.is() behaves exactly like === except for these two cases:
Complete Comparison Table
When to Use Object.is()
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
Complete Results Table
The Famous Quirks
typeof null === 'object' — A Famous Bug
typeof null === 'object' — A Famous Bug
000, and null was represented as 0x00, which also matched the object tag.Why wasn’t it fixed? Too much existing code depends on this behavior. Changing it now would break millions of websites. So we have to work around it.Workaround:Arrays Return 'object'
Arrays Return 'object'
Object.prototype and have special behavior for numeric keys and the length property, but they’re still objects.How to check for arrays:Array.isArray(). It’s the most reliable method.Functions Return 'function'
Functions Return 'function'
typeof treats them specially because checking for “callable” values is so common. This is actually convenient!typeof on Undeclared Variables
typeof on Undeclared Variables
NaN is a 'number' — Yes, Really
NaN is a 'number' — Yes, Really
"number". This sounds strange, but NaN is actually a special value in the number system. It represents a calculation that doesn’t have a valid result, like 0 / 0.Better Alternatives for Type Checking
Sincetypeof has limitations, here are more reliable approaches:
- Type-Specific Checks
- instanceof
- Object.prototype.toString
- Custom Type Checker
Number are more reliable than typeof for numeric checks.Decision Guide: Which to Use?
The Simple Rule
=== 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
Quick Reference
ESLint Configuration
Most style guides enforce=== with an exception for null checks:
Common Gotchas and Mistakes
These common mistakes trip up many JavaScript developers. Learning about them now will save you debugging time later:1. Comparing Objects by Value
1. Comparing Objects by Value
2. Truthy/Falsy Confusion with ==
2. Truthy/Falsy Confusion with ==
== operator doesn’t check truthiness. It performs type coercion according to specific rules.The fix:3. NaN Comparisons
3. NaN Comparisons
NaN is never equal to anything, including itself.The fix:4. The typeof null Trap
4. The typeof null Trap
typeof null === "object" is true due to a historical bug.The fix:5. String Comparison Gotchas
5. String Comparison Gotchas
6. Empty Array Comparisons
6. Empty Array Comparisons
== uses type coercion ([] → "" → 0), but truthiness just checks if the value is truthy (all objects are truthy).The fix:Common Misconceptions
Misconception 1: '== is always bad and should never be used'
Misconception 1: '== is always bad and should never be used'
=== should be your default, there’s one legitimate use case for ==:eqeqeq rule with the "null": "ignore" option).Misconception 2: '=== checks if types are the same'
Misconception 2: '=== checks if types are the same'
=== doesn’t just check types. It checks if two values are the same type AND same value.=== returns false immediately if types differ, then compares values if types match.Misconception 3: 'typeof is reliable for checking all types'
Misconception 3: 'typeof is reliable for checking all types'
typeof has several well-known quirks:- Use
Array.isArray()for arrays - Use
value === nullfor null - Use
Number.isNaN()for NaN - Use
Object.prototype.toString.call()for precise type detection
Misconception 4: 'Objects with the same content are equal'
Misconception 4: 'Objects with the same content are equal'
JSON.stringify() for simple cases or a deep equality function like Lodash’s _.isEqual().Misconception 5: 'NaN means the value is not a number'
Misconception 5: 'NaN means the value is not a number'
NaN is actually a numeric value that represents an undefined or unrepresentable mathematical result:NaN as “the result of a calculation that doesn’t produce a meaningful number” rather than literally “not a number.”Misconception 6: 'Truthy values are == true'
Misconception 6: 'Truthy values are == true'
== equality are completely different concepts:== true or == false. Either use === or just rely on truthiness directly: if (value).Key Takeaways
-
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
== and === for all type combinations. Hover over cells to see explanations. An essential reference for understanding JavaScript equality!- 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)
Test Your Knowledge
Try to answer each question before revealing the solution:Question 1: What is the output of [] == ![] ?
Question 1: What is the output of [] == ![] ?
trueStep-by-step:![]→false(arrays are truthy, so negation makes false)[] == false→[] == 0(boolean converts to number)[] == 0→"" == 0(array converts to empty string)"" == 0→0 == 0(string converts to number)0 == 0→true
Question 2: Why does typeof null return 'object'?
Question 2: Why does typeof null return 'object'?
000, and null was represented as the NULL pointer (0x00), which also matched the 000 tag for objects.This bug was never fixed because too much existing code depends on this behavior. A proposal to fix it was rejected for backward compatibility reasons.Workaround: Always check for null explicitly: value === nullQuestion 3: How would you properly check if a value is NaN?
Question 3: How would you properly check if a value is NaN?
Number.isNaN():isNaN() because it coerces its argument first:Object.is(value, NaN) which returns true for NaN.Question 4: What's the ONE legitimate use case for ==?
Question 4: What's the ONE legitimate use case for ==?
null and undefined in a single comparison:null == undefined is true (special case in the spec), but null and undefined don’t loosely equal anything else (not even 0, "", or false).Question 5: Why does {} === {} return false?
Question 5: Why does {} === {} return false?
{}, JavaScript creates a new object in memory. When you write another {}, it creates a completely different object. Even though they have the same content (both are empty objects), they are stored at different memory locations.Question 6: What's the difference between === and Object.is()?
Question 6: What's the difference between === and Object.is()?
=== for everyday comparisons. Use Object.is() when you specifically need to check for NaN equality or distinguish positive from negative zero.Question 7: How would you reliably check if something is an array?
Question 7: How would you reliably check if something is an array?
Array.isArray():typeof? Because typeof [] === "object". Arrays are objects in JavaScript.Why not instanceof Array? It works in most cases, but can fail across different JavaScript realms (like iframes) where each realm has its own Array constructor.Frequently Asked Questions
What is the difference between == and === in JavaScript?
What is the difference between == and === in JavaScript?
== (loose equality) operator converts values to the same type before comparing, while === (strict equality) compares both type and value without any conversion. For example, 5 == "5" is true because the string is coerced to a number, but 5 === "5" is false because a number and string are different types. According to the ECMAScript specification, === should be your default choice for reliable comparisons.Why does NaN not equal itself in JavaScript?
Why does NaN not equal itself in JavaScript?
0/0 or Math.sqrt(-1)), so it cannot meaningfully equal any value — including itself. Use Number.isNaN() to check for NaN reliably.When should I use == instead of === in JavaScript?
When should I use == instead of === in JavaScript?
== is checking for null or undefined in a single expression: if (value == null). This catches both null and undefined because the ECMAScript specification defines null == undefined as true. Most linters, including ESLint’s eqeqeq rule, allow this specific pattern while enforcing === everywhere else.What is Object.is() and when should I use it?
What is Object.is() and when should I use it?
Object.is() is an ES6 method that implements the “SameValue” equality algorithm. It behaves like === except in two cases: Object.is(NaN, NaN) returns true (unlike ===), and Object.is(+0, -0) returns false (unlike ===). Use it when you need to distinguish positive and negative zero or reliably detect NaN.Why does typeof null return 'object' in JavaScript?
Why does typeof null return 'object' in JavaScript?
000 tag. A TC39 proposal to fix this was rejected because changing it would break backward compatibility with millions of existing websites. Always check for null explicitly with value === null.Related Concepts
Type Coercion
Primitive Types
Primitives vs Objects
Scope and Closures
Reference
Equality comparisons and sameness — MDN
typeof — MDN
Articles
JavaScript Double Equals vs. Triple Equals — Brandon Morelli
What is the difference between == and === in JavaScript? — Craig Buckler
=== vs == Comparison in JavaScript — FreeCodeCamp
true == "true" returns false.