在 ES6 中,是否可以克隆迭代器状态?
var ma=[1,2,3,4];
var it=ma[Symbol.iterator]();
it.next();
如果我想记住这里它说明了我应该在javascritp中做什么?
里面记着什么? 自从
JSON.stringify(it) //it would just return {}
您无法克隆任意迭代器,但您可以通过保留某种状态来创建许多不同的迭代器:
function tee(iterable) {
const source = iterable[Symbol.iterator]();
const buffers = [[], []]; // substitute in queue type for efficiency
const DONE = Object.create(null);
const next = i => {
if (buffers[i].length !== 0) {
return buffers[i].shift();
}
const x = source.next();
if (x.done) {
return DONE;
}
buffers[1 - i].push(x.value);
return x.value;
};
return buffers.map(function* (_, i) {
for (;;) {
const x = next(i);
if (x === DONE) {
break;
}
yield x;
}
});
}
用途:
const [a, b] = tee(iterator);
assert(a.next().value === b.next().value);
无法克隆迭代器。迭代器状态基本上是完全任意的,任何给定的迭代器都可能需要或产生副作用(例如,读取或写入网络流),这些副作用不可按需重复。
这还不是正式的,但我认为在 Iterator Helpers 的第二阶段提案中可能有一个解决方案。如果这些方法不影响原始迭代器,那么执行诸如
iter.take(Infinity)
或 iter.drop(0)
之类的操作将具有与克隆相同的效果。
我构建了一个库,允许您在这里分叉迭代器:https://github.com/tjenkinson/forkable-iterator
意味着你可以做类似的事情:
import { buildForkableIterator, fork } from 'forkable-iterator';
function* Source() {
yield 1;
yield 2;
return 'return';
}
const forkableIterator = buildForkableIterator(Source());
console.log(forkableIterator.next()); // { value: 1, done: false }
const child1 = fork(forkableIterator);
// { value: 2, done: false }
console.log(child1.next());
// { value: 2, done: false }
console.log(forkableIterator.next());
// { value: 'return', done: true }
console.log(child1.next());
// { value: 'return', done: true }
console.log(forkableIterator.next());
如果您不再需要继续从 fork 中消费,并且提供了对它的松散引用,那么也不应该出现内存泄漏。
您可以创建多个依赖于原始迭代器的迭代器。使用缓冲来跟踪哪些值已经被消耗以服务于这些新迭代器之一,但还不是全部。
这里是一个使用链表作为缓冲区的实现,这样所有返回的迭代器都可以使用同一个列表:
function tee(iterable, n=2) {
const it = iterable[Symbol.iterator]();
function* gen(current) {
while (true) {
if (!current.next) { // when at the tail of the linked list
const {done, value} = it.next(); // fetch a new value and
if (done) return;
current.next = { value }; // append it to the linked list
}
current = current.next; // Move up to the next node
yield current.value;
}
}
// All generators start at the same node {}, i.e. the empty linked list
return Array(n).fill({}).map(gen);
}
// Example usage
const [a, b] = tee(Array(10).keys());
console.log("a", a.next().value);
console.log("b", b.next().value, b.next().value);
console.log("a", a.next().value, a.next().value, a.next().value);
console.log("b", b.next().value, b.next().value, b.next().value);
console.log("a leftover", ...a);
console.log("b leftover", ...b);
一个很好的效果是,一旦最慢的迭代器消耗了一个值,这个链表中的节点就会成为垃圾收集的候选者。