Is it safe to delete elements in a Set while iterating with for..of?

后端 未结 2 1573
情歌与酒
情歌与酒 2021-02-06 23:43

Is it specified that you can delete any element in an instance of Set while iterating using for..of and that

  • you won\'t iterate more than
2条回答
  •  误落风尘
    2021-02-07 00:16

    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.

    Intuitively:

    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.


    Formally

    Addition

    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.

    With code

    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;
    }

提交回复
热议问题