How JavaScript Set Enforces Value Uniqueness

The JavaScript Set collection is designed to store distinct elements, automatically rejecting duplicate entries upon insertion. It enforces this uniqueness across primitive values and reference objects by relying internally on the SameValueZero equality algorithm. While primitive types are compared strictly by their actual value, non-primitive objects are compared by their memory reference, leading to different deduplication behaviors depending on the type of data being added.

The SameValueZero Algorithm

When you add a value using Set.prototype.add(), JavaScript checks if the value already exists in the set using the SameValueZero algorithm. This algorithm behaves almost identically to the strict equality operator (===), with two primary differences:

  1. NaN Handling: In standard JavaScript, NaN === NaN evaluates to false. However, the SameValueZero algorithm treats all NaN values as equal to each other. As a result, a Set can only contain a single NaN value.
  2. Signed Zeros: Both +0 and -0 are considered equal, meaning a Set will not store both +0 and -0 simultaneously.

Uniqueness in Primitive Values

Primitives—such as Number, String, Boolean, Symbol, BigInt, null, and undefined—are compared by their value. If you attempt to insert an identical primitive that already exists in the Set, the operation is silently ignored.

const set = new Set();

set.add(42);
set.add(42); // Duplicate ignored
set.add('hello');
set.add('hello'); // Duplicate ignored
set.add(NaN);
set.add(NaN); // Duplicate ignored

console.log(set.size); // Output: 3 (contains: 42, 'hello', NaN)

Uniqueness in Objects and References

Non-primitive values, including plain objects, arrays, and functions, are stored and compared by reference identity, not by structural or deep equality.

Two distinct objects with identical keys and values occupy different locations in memory. Because SameValueZero checks reference identity rather than structural similarity, both objects will be treated as unique values.

const set = new Set();

const objA = { id: 1 };
const objB = { id: 1 };

set.add(objA);
set.add(objB); // Added because objB points to a different memory address
set.add(objA); // Duplicate reference ignored

console.log(set.size); // Output: 2

To enforce uniqueness for complex objects based on their content rather than their reference, objects must either share the exact same variable reference or be serialized (e.g., via JSON.stringify) into primitive strings before being added to the set.