What is the difference between == and === in JavaScript and when is it better to use which operator?

👨‍💻 Frontend Developer 🟢 Almost Certain 🎚️ Easy
#JavaScript #JS Basics #Operators

Quick Answer

The == operator (loose equality) compares values with type coercion, while the === operator (strict equality) compares values and types without coercion:

5 == "5";   // true (converts string to number)
5 === "5";  // false (different types)
 
null == undefined;  // true (special case)
null === undefined; // false (different types)
 
0 == false;   // true (converts boolean to number)
0 === false;  // false (different types)

Recommendation: Use === in 99% of cases for predictable behavior.


How Comparison Operators Work

Strict Equality (===)

// Compares type AND value
console.log(5 === 5);        // true
console.log(5 === "5");      // false (different types)
console.log(true === true);  // true
console.log(true === 1);     // false (different types)
console.log(null === null);  // true
console.log(undefined === undefined); // true
console.log(null === undefined);      // false

Loose Equality (==)

// Compares values with type coercion
console.log(5 == "5");       // true ("5" → 5)
console.log(true == 1);      // true (true → 1)
console.log(false == 0);     // true (false → 0)
console.log(null == undefined); // true (special case)
console.log("" == 0);        // true ("" → 0)

Comparison Table

Comparison== (loose)=== (strict)Explanation
5 == "5"truefalseString is converted to number
true == 1truefalseBoolean is converted to number
false == 0truefalseBoolean is converted to number
null == undefinedtruefalseSpecial rule for ==
"" == 0truefalseEmpty string is converted to 0
" " == 0truefalseSpace is converted to 0
"0" == 0truefalseString is converted to number
[] == 0truefalseArray is converted to string, then to number
[] == ""truefalseArray is converted to empty string
[1] == 1truefalseArray is converted to string “1”, then to number

Type Coercion Algorithm for ==

Coercion Rules

  1. Same types → comparison like with ===
  2. null and undefined → always equal to each other
  3. Number and string → string is converted to number
  4. Boolean → converted to number (true → 1, false → 0)
  5. Object and primitive → object is converted to primitive
// Type coercion examples
console.log("5" == 5);     // "5" → 5, then 5 == 5 → true
console.log(true == "1");  // true → 1, "1" → 1, then 1 == 1 → true
console.log([1,2] == "1,2"); // [1,2] → "1,2", then "1,2" == "1,2" → true

Special Cases

⚠️ Warning! These comparisons can lead to unexpected results:

// Paradoxes with ==
console.log("" == 0);        // true
console.log(" " == 0);       // true  
console.log("\n" == 0);      // true
console.log("\t" == 0);      // true
 
// Transitivity violated!
console.log("" == 0);        // true
console.log(0 == false);     // true
console.log("" == false);    // true, but logically strange
 
// Arrays
console.log([] == 0);        // true
console.log([] == false);    // true
console.log([] == "");       // true

Practical Examples

1. Checking for null/undefined

// ❌ Bad: unexpected behavior
function checkValue(value) {
  if (value == null) {
    return "Empty value"; // Will work for null AND undefined
  }
  return "Has value";
}
 
console.log(checkValue(null));      // "Empty value"
console.log(checkValue(undefined)); // "Empty value"
console.log(checkValue(0));         // "Has value"
console.log(checkValue(""));        // "Has value"
 
// ✅ Good: explicit check
function checkValueStrict(value) {
  if (value === null || value === undefined) {
    return "Empty value";
  }
  return "Has value";
}
 
// ✅ Or use nullish coalescing
const result = value ?? "default value";

2. Comparing User Input

// ❌ Bad: unexpected results
function validateAge(input) {
  if (input == 18) {
    return "Adult";
  }
  return "Minor";
}
 
console.log(validateAge("18"));    // "Adult" (works by accident)
console.log(validateAge(" 18 "));  // "Minor" (spaces!)
console.log(validateAge(true));    // "Minor" (true != 18)
 
// ✅ Good: explicit type conversion
function validateAgeStrict(input) {
  const age = Number(input);
  if (age === 18) {
    return "Adult";
  }
  return "Minor";
}

3. Working with Forms

// ❌ Bad: checkbox can be a string
function handleCheckbox(isChecked) {
  if (isChecked == true) {
    console.log("Checked");
  }
}
 
handleCheckbox("true");  // Won't work!
handleCheckbox(1);       // Will work (1 == true)
 
// ✅ Good: explicit check
function handleCheckboxStrict(isChecked) {
  if (isChecked === true || isChecked === "true") {
    console.log("Checked");
  }
}
 
// ✅ Or convert to boolean
function handleCheckboxBoolean(isChecked) {
  if (Boolean(isChecked)) {
    console.log("Checked");
  }
}

When to Use == vs ===

Use === (strict equality)

In 99% of cases — it’s the safe choice:

// ✅ Safe comparisons
if (user.age === 18) { /* ... */ }
if (status === "active") { /* ... */ }
if (count === 0) { /* ... */ }
if (value === null) { /* ... */ }
if (typeof data === "string") { /* ... */ }
 
// ✅ In arrays and objects
const users = ["admin", "user", "guest"];
if (users.includes("admin")) { /* ... */ } // includes uses ===
 
// ✅ In switch (uses ===)
switch (userRole) {
  case "admin":
    // ...
    break;
}

Rare Cases for ==

Only when you know exactly what you’re doing:

// ✅ Check for null/undefined simultaneously
if (value == null) {
  // Will work for null AND undefined
  console.log("Value is empty");
}
 
// Equivalent to:
if (value === null || value === undefined) {
  console.log("Value is empty");
}
 
// ✅ But better to use nullish coalescing
const result = value ?? "default";

Practical Tasks

Task 1: What will the console output?

console.log(0 == false);
console.log(0 === false);
console.log("" == false);
console.log("" === false);
Answer

true, false, true, false

  • 0 == falsefalse is converted to 0, then 0 == 0true
  • 0 === false → different types → false
  • "" == falsefalse is converted to 0, "" is converted to 0, then 0 == 0true
  • "" === false → different types → false

Task 2: Find the error

function isAdult(age) {
  return age == 18;
}
 
console.log(isAdult("18"));   // ?
console.log(isAdult(" 18"));  // ?
console.log(isAdult(18.0));   // ?
Answer

true, false, true

Problem: " 18" (with space) is not equal to 18 even with type coercion.

Solution:

function isAdult(age) {
  return Number(age) === 18;
}

Task 3: What will the code output?

const arr = [];
console.log(arr == 0);
console.log(arr == "");
console.log(arr == false);
console.log(arr === false);
Answer

true, true, true, false

  • Empty array [] is converted to empty string ""
  • Empty string is converted to 0
  • false is converted to 0
  • Therefore all comparisons with == give true
  • But === compares types, so false

Task 4: Fix the function

// ❌ Problematic function
function findUser(users, id) {
  return users.find(user => user.id == id);
}
 
const users = [
  { id: 1, name: "Anna" },
  { id: "2", name: "Boris" },
  { id: 3, name: "Vera" }
];
 
console.log(findUser(users, "1")); // Will find Anna (1 == "1")
console.log(findUser(users, 2));   // Will find Boris ("2" == 2)
Answer
// ✅ Fixed function
function findUser(users, id) {
  // Convert id to string for consistency
  const searchId = String(id);
  return users.find(user => String(user.id) === searchId);
}
 
// Or convert to number
function findUserById(users, id) {
  const searchId = Number(id);
  return users.find(user => Number(user.id) === searchId);
}
 
// Or use strict comparison with type check
function findUserStrict(users, id) {
  return users.find(user => user.id === id);
}

Task 5: Form Validation

function validateForm(data) {
  // Find problems in this validation
  if (data.name == "") {
    return "Name is required";
  }
  if (data.age == 0) {
    return "Age is required";
  }
  if (data.isActive == false) {
    return "Account must be active";
  }
  return "Validation passed";
}
 
// Test data
console.log(validateForm({ name: 0, age: false, isActive: "" }));
Answer

Problems:

  • name: 0 will pass the check name == "" (0 != "")
  • age: false will not pass the check age == 0 (false == 0 → true)
  • isActive: "" will not pass the check isActive == false ("" != false)

Fixed version:

function validateForm(data) {
  if (typeof data.name !== "string" || data.name.trim() === "") {
    return "Name is required";
  }
  if (typeof data.age !== "number" || data.age <= 0) {
    return "Age must be a positive number";
  }
  if (data.isActive !== true) {
    return "Account must be active";
  }
  return "Validation passed";
}

Best Practices

1. Always Use ===

// ✅ Good
if (status === "active") { /* ... */ }
if (count === 0) { /* ... */ }
if (user === null) { /* ... */ }
 
// ❌ Bad
if (status == "active") { /* ... */ }
if (count == 0) { /* ... */ }
if (user == null) { /* ... */ }

2. Explicit Type Conversion

// ✅ Good: explicit conversion
const userInput = "25";
const age = Number(userInput);
if (age === 25) { /* ... */ }
 
// ❌ Bad: implicit conversion
if (userInput == 25) { /* ... */ }

3. Type Checking

// ✅ Good: check type
function processValue(value) {
  if (typeof value === "string" && value.length > 0) {
    return value.toUpperCase();
  }
  if (typeof value === "number" && value > 0) {
    return value * 2;
  }
  return null;
}
 
// ❌ Bad: rely on type coercion
function processValueBad(value) {
  if (value == true) {
    return value.toUpperCase(); // May crash!
  }
  return value * 2;
}

4. Use ESLint Rules

// .eslintrc.json
{
  "rules": {
    "eqeqeq": ["error", "always"], // Forbids ==
    "no-implicit-coercion": "error" // Forbids implicit coercion
  }
}

Modern Alternatives

Object.is() — “Super Strict” Comparison

// Object.is() is even stricter than ===
console.log(Object.is(NaN, NaN));     // true (=== gives false)
console.log(Object.is(-0, +0));       // false (=== gives true)
console.log(Object.is(5, 5));         // true
console.log(Object.is(5, "5"));       // false
 
// Useful for special cases
function isActuallyNaN(value) {
  return Object.is(value, NaN);
}

Nullish Coalescing (??)

// Instead of checking for null/undefined
const result = value ?? "default";
 
// Instead of
const result2 = (value === null || value === undefined) 
  ? "default" 
  : value;

Optional Chaining (?.)

// Safe property access
const city = user?.address?.city;
 
// Instead of
const city2 = user && user.address && user.address.city;

Common Mistakes

1. Comparing with Boolean

// ❌ Bad
if (isActive == true) { /* ... */ }
if (hasPermission == false) { /* ... */ }
 
// ✅ Good
if (isActive === true) { /* ... */ }
if (isActive) { /* ... */ }           // Even better
if (!hasPermission) { /* ... */ }     // For false

2. Comparing Arrays and Objects

// ❌ Always false (references are compared)
console.log([] == []);     // false
console.log({} == {});     // false
 
// ✅ Proper array comparison
function arraysEqual(a, b) {
  return a.length === b.length && 
         a.every((val, i) => val === b[i]);
}
 
// ✅ Proper object comparison
function objectsEqual(a, b) {
  return JSON.stringify(a) === JSON.stringify(b); // Simple way
  // Or use lodash.isEqual library
}

3. Checking Property Existence

const obj = { count: 0, name: "" };
 
// ❌ Bad: 0 and "" are considered false
if (obj.count) { /* ... */ }     // Won't work for 0
if (obj.name) { /* ... */ }      // Won't work for ""
 
// ✅ Good: check existence
if ("count" in obj) { /* ... */ }
if (obj.hasOwnProperty("count")) { /* ... */ }
if (obj.count !== undefined) { /* ... */ }

Conclusion

Golden Rules:

  1. Use === in 99% of cases — it’s safe and predictable
  2. Avoid == — it can lead to unexpected results
  3. Make type conversion explicit — use Number(), String(), Boolean()
  4. Check types — use typeof, Array.isArray(), instanceof
  5. Use modern features??, ?., Object.is()

Remember: Code should be understandable not only to the computer, but also to other developers. Strict equality === makes your intentions clear and prevents errors.


Want more articles for interview preparation? Subscribe to EasyAdvice, bookmark the site and level up every day 💪