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:
NaNHandling: In standard JavaScript,NaN === NaNevaluates tofalse. However, theSameValueZeroalgorithm treats allNaNvalues as equal to each other. As a result, aSetcan only contain a singleNaNvalue.- Signed Zeros: Both
+0and-0are considered equal, meaning aSetwill not store both+0and-0simultaneously.
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: 2To 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.