When to Use Object.is for Precise Value Comparisons in JavaScript


What is Object.is?

In JavaScript, Object.is is a method that determines whether two values are strictly equal. It's a more precise comparison method compared to the standard equality operators (== and ===).

How is it different from ===?

  • Object.is
    Similar to === but with stricter handling of zeros and NaN:
    • Object.is(-0, +0) returns false (unlike === which returns true).
    • Object.is(NaN, NaN) returns true (unlike === which returns false).
  • === (Strict Equality)
    Performs a type check and value check. It treats positive and negative zero (+0 and -0) as equal, but considers NaN (Not a Number) not equal to itself.
Comparison=== (Strict Equality)Object.is
-0 === +0truefalse
NaN === NaNfalsetrue

When to use Object.is?

Use Object.is when you need a very strict equality check, especially when dealing with zeros or NaN values and want to avoid potential type coercion issues that can occur with ===.

Example

const num1 = -0;
const num2 = +0;

console.log(num1 === num2); // true (might be surprising)
console.log(Object.is(num1, num2)); // false (more accurate)

const nan1 = NaN;
const nan2 = NaN;

console.log(nan1 === nan2); // false
console.log(Object.is(nan1, nan2)); // true
  • It's generally recommended for situations where you need a more predictable equality check, particularly when working with zeros and NaN.
  • Object.is is not a part of the core JavaScript language; it's a method defined on the built-in Object object.


Checking for exact equality of floating-point numbers

function areFloatingPointEqual(num1, num2) {
  // Handle cases where either number is NaN (Not a Number)
  if (isNaN(num1) || isNaN(num2)) {
    return Object.is(num1, num2);
  }

  // Use Object.is for strict comparison, avoiding potential rounding errors
  return Object.is(+num1, +num2); // Convert to numbers explicitly
}

const a = 0.1 + 0.2;
const b = 0.3;

console.log(a === b); // Might be false due to floating-point precision issues
console.log(Object.is(a, b)); // Also false
console.log(areFloatingPointEqual(a, b)); // More reliable check (false)

Comparing object references

const obj1 = {};
const obj2 = {};

console.log(obj1 === obj2); // false (different object references)
console.log(Object.is(obj1, obj2)); // false

// Even if objects have the same properties, they're not the same object
const obj3 = { name: "Alice" };
const obj4 = { name: "Alice" };

console.log(obj3 === obj4); // false
console.log(Object.is(obj3, obj4)); // false
const str1 = "10";
const num1 = 10;

console.log(str1 === num1); // true (implicit type coercion)
console.log(Object.is(str1, num1)); // false (strict check)

const bool1 = true;
const str2 = "true";

console.log(bool1 === str2); // false (different types)
console.log(Object.is(bool1, str2)); // false


Strict Equality Operator (===)

  • However, be aware of its behavior with zeros and NaN:
    • -0 === +0 returns true.
    • NaN === NaN returns false.
  • In most cases, the strict equality operator (===) is sufficient for general comparison tasks. It performs both type and value checks, making it suitable for most comparisons.

Custom Comparison Functions

  • This allows you to define specific rules for handling different data types or edge cases.
  • If you need more control over the comparison logic, write a custom function.
function compareFloats(num1, num2, tolerance = 0.0001) {
  return Math.abs(num1 - num2) < tolerance;
}

const a = 0.1 + 0.2;
const b = 0.3;

console.log(compareFloats(a, b)); // Could be true depending on tolerance

Lodash's isEqual Function (if using a library)

  • Be mindful of potential library size and dependency considerations.
  • These functions can handle complex data structures like objects and arrays more comprehensively.
  • If you're already using a library like Lodash, it might provide a helper function like isEqual for deep comparisons.
  • For more complex comparisons or handling edge cases, consider custom functions or appropriate library functions.
  • For general equality checks, === usually suffices.
  • If strict equality checks with the specific handling of zeros and NaN from Object.is are crucial, it's the recommended choice.