Is it specified that you can delete any element in an instance of Set
while iterating using for..of
and that
Yes, it is perfectly fine to add elements and remove elements to a set while iterating it. This use case was considered and is supported in JavaScript 2015 (ES6). It will leave it in a consistent state. Note this also applies to itearting with forEach
.
The set iteration algorithm basically looks something like this:
Set position to 0
While position < calculateLength() // note it's calculated on each iteration
return the element at set.entryList[position]
Addition just looks something like this:
If element not in set
Add element to the _end_ of the set
So it does not interfere with existing iterations - they will iterate it.
Deletion looks something like this:
Replace all elements with are equal to `element` with a special empty value
Replacing it with an empty value rather than deleting it ensures it will not mess up with iterators' positions.
Here is the relevant part of the specification from %SetIteratorPrototype%.next
:
Repeat while index is less than the total number of elements of entries. The number of elements must be redetermined each time this method is evaluated.
The set iterator proceeds to iterate the entries one by one.
From Set.prototype.add
:
Append value as the last element of entries.
This ensures that when adding elements to the list it will be iterated before the iteration completes since it always gets a new slot in the entries list. Thus this will work as the spec mandates.
As for deletion:
Replace the element of entries whose value is e with an element whose value is empty.
Replacing it with an empty element rather than removing it ensures that the iteration order of existing iterators will not get out or order and they will continue iterating the set correctly.
Here is a short code snippet that demonstrates this ability
var set = new Set([1]);
for(let item of set){
if(item < 10) set.add(item+1);
console.log(item);
}
Which logs the numbers 1 to 10. Here is a version not using for... of you can run in your browser today:
var set = new Set([1]);
for (var _i = set[Symbol.iterator](), next; !(next = _i.next()).done;) {
var item = next.value;
if (item < 10) set.add(item + 1);
document.body.innerHTML += " " + item;
}