Set.prototype.add()
Inserts a value if it is not already present, and returns the same collection so calls can chain.
add(value)- Added in JavaScript ES2015
Parameters
| Parameter | Type | Description |
|---|---|---|
$value | any | The value to add. Objects are compared by reference, not by value. If an equal value (SameValueZero) is already present, the collection is left unchanged. |
Return value
SetThe same Set object, which makes calls chainable.
Errors
Throws a TypeError if called on a value that is not a real Set instance.
add() is the only way to grow a Set after construction. It is idempotent: adding a value that is already a member does nothing and is not an error, so you never need to guard it with a has() check. Writing if (!s.has(x)) s.add(x) is redundant work.
The return value is the collection itself, not a boolean and not the value. That is what makes s.add(1).add(2).add(3) legal, and it is also the thing that surprises people: there is no way to learn from the return value whether the insert actually changed anything. If you need that, compare size before and after, or call has() first.
Ordering
Insertion order is preserved and stable. Re-adding an existing value does not move it to the end — the position from the first successful insert is kept. If you want most-recently-used ordering (an LRU cache, for instance), delete the value and add it again.
Adding during iteration
A value added while a for...of loop or forEach() is still running will be visited by that same loop. This is well defined, but it makes an unconditional add inside a loop over the same collection an infinite loop.
Example
When to use it
- Accumulate the distinct error codes seen while validating a batch
- Mark graph nodes visited inside a breadth-first search
- Build a tag collection from user clicks without checking for repeats
- Collect the unique keys present across a list of heterogeneous objects
Frequently asked questions
Does add() overwrite a value that is already in the Set?
No — if an equal value is already present, the collection is left completely unchanged and the original stays in its original insertion position. The call is a silent no-op, not an error.
Why does add() return the Set instead of true or false?
So that calls can be chained, as in s.add(1).add(2). To find out whether the value was actually new, compare size before and after the call, or test with has() first.
Can I add to a Set while looping over it?
Yes, and the running loop will visit the newly added values — which means an unconditional add inside a for...of over the same collection never terminates. Iterate a copy if you need to add safely.
More examples
Detecting whether the add was new
Since add() always returns the Set, the size comparison is the cheapest way to detect a real insertion. The output shows false for the duplicate "a" and true for the new "b".
const seen = new Set(["a"]);
function addNew(set, value) {
const before = set.size;
set.add(value);
return set.size !== before;
}
console.log(addNew(seen, "a"));
console.log(addNew(seen, "b"));
console.log([...seen]);Objects go in by reference
The same reference added twice counts once, but a fresh literal with identical contents counts separately, giving size 2. Mutating a stored object after insertion is visible through the Set — it holds the reference, not a snapshot.
const shapes = new Set();
const circle = { kind: "circle" };
shapes.add(circle);
shapes.add(circle);
shapes.add({ kind: "circle" });
console.log(shapes.size);
console.log(shapes.has(circle));
console.log(shapes.has({ kind: "circle" }));
circle.kind = "ellipse";
console.log([...shapes][0]);Refreshing recency by delete-then-add
A plain add() on an existing member keeps its original position, so "alpha" only moves to the end once it has been deleted first. This delete-then-add pair is the standard trick for maintaining most-recently-used order.
const recent = new Set(["alpha", "beta", "gamma"]);
function touch(set, value) {
set.delete(value);
set.add(value);
}
touch(recent, "alpha");
console.log([...recent]);
touch(recent, "delta");
console.log([...recent]);
Discussion