在使用for..of
迭代一个Set
实例时,是否规定可以删除任意元素,并且:
- 您不会对某个元素进行多次遍历
- 除了您删除的元素之外,不会错过在迭代开始时存在于集合中的任何其他元素
?
在使用for..of
迭代一个Set
实例时,是否规定可以删除任意元素,并且:
?
是的,在迭代集合时添加或删除元素是完全可以的。在JavaScript 2015(ES6)中,已经考虑到并支持了这种用例,并且会保持集合处于一致的状态。请注意,这也适用于使用forEach
进行迭代。
集合迭代算法基本上看起来像这样:
Set position to 0
While position < calculateLength() // note it's calculated on each iteration
return the element at set.entryList[position]
加法的样子类似于这样:
If element not in set
Add element to the _end_ of the set
因此,它不会干扰现有迭代-它们将迭代它。
删除的过程如下所示:
Replace all elements with are equal to `element` with a special empty value
用空值替换它而不是删除它可以确保不会破坏迭代器的位置。
这里是来自% SetIteratorPrototype% .next
规范的相关部分:
当索引小于条目总数时重复。必须每次在计算此方法时重新确定元素数量。
集合迭代器逐个迭代条目。
来自Set.prototype.add
:
将值作为最后一个元素附加到条目中。
这确保了在向列表添加元素时,在迭代完成之前将进行迭代,因为它总是在条目列表中获得新插槽。因此,这将按照规范要求工作。
至于删除:
用一个值为空的元素替换值为e的元素。
用空元素替换而不是移除它可以确保现有迭代器的迭代顺序不会失序,并且它们将继续正确地迭代集合。
这是一个演示这种能力的简短代码片段
var set = new Set([1]);
for(let item of set){
if(item < 10) set.add(item+1);
console.log(item);
}
记录数字1到10的日志。下面是一个不使用for...of循环的版本,您可以在浏览器中运行它:
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;
}
Symbol
在FF中尚未得到支持,因为消费者版本是35,而它需要36。 - nduggerforEach
迭代时插入和删除元素的情况。但是规范似乎并没有要求这样做。目前看来,JS 引擎已经一致地实现了它。 - NathanHere's my test code:
s = new Set([ { a: 0 }, { a: 1 }, { a: 2 }, { a: 3 } ]);
do {
for (let x of s) {
console.log(x.a);
if (Math.random() < 0.2) {
console.log('deleted ' + x.a);
s.delete(x);
}
}
} while (s.size > 0);
0
1
2
3
0
1
deleted 1
2
3
0
2
deleted 2
3
0
3
0
deleted 0
3
3
...
3
3
deleted 3
我也用类似的代码进行了测试,但它没有使用当前迭代过程的实例:
sCopy = [{ a: 0 }, { a: 1 }, { a: 2 }, { a: 3 }];
s = new Set(sCopy);
do {
for (let x of s) {
console.log(x.a);
if (Math.random() < 0.2) {
let deleteMe = Math.floor(Math.random() * s.size);
console.log('deleted ' + sCopy[deleteMe].a);
s.delete(sCopy[deleteMe]);
sCopy.splice(deleteMe, 1);
}
}
} while (s.size > 0);
0
deleted 1
2
deleted 2
3
0
3
0
deleted 0
3
3
3
3
deleted 3
是的,我甚至测试了随机对象实例插入... 结果一样,这次我不会发布输出:
sCopy = [{ a: 0 }, { a: 1 }, { a: 2 } ];
s = new Set(sCopy);
do {
for (let x of s) {
console.log(x.a);
if (Math.random() < 0.1) {
let newInstance = { a: Math.random() * 100 + 100 };
console.log('added ' + newInstance.a);
s.add(newInstance);
sCopy.push(newInstance);
}
if (Math.random() < 0.2) {
let deleteMe = Math.floor(Math.random() * s.size);
console.log('deleted ' + sCopy[deleteMe].a);
s.delete(sCopy[deleteMe]);
sCopy.splice(deleteMe, 1);
}
}
} while (s.size > 0);