new Set(getObjects())
getObjects
返回一个可迭代对象。每次迭代都会创建一个新对象。但是 Set
立即获取并添加所有新对象。
如何使得只有当需要新对象时,才获取并添加新对象?
我尝试了 Proxy
,但在使用 receiver
作为 this
调用函数时,没有处理程序触发。
const getEntitySet = (nativeEntityCollection) => {
const cache = new Set;
let src = {
[Symbol.iterator]: ((itr) => {
return () => itr;
})(nativeEntityCollection[Symbol.iterator]()),
ref: nativeEntityCollection
};
let addAll = () => {
for (const entity of src) {
cache.add(entity)
}
done()
};
let generate = function*() {
yield* cache;
for (const entity of src) {
cache.add(entity);
yield entity;
}
done()
};
const done = () => {
src = null;
addAll = null;
generate = Reflect.get(cache, Symbol.iterator).bind(cache)
};
const generateRef = () => generate();
return new Proxy(cache, {
get(set, prop) {
if (prop === Symbol.iterator) {
return generateRef;
}
addAll?.();
return Reflect.get(cache, prop);
}
});
};
最佳答案
当在代理对象上使用 Symbol.iterator
以外的其他方法时,可以重现您提到的接收器问题,例如 has
。
这可以通过使用 bind
来修复(就像您在其他地方已经做的那样):
return Reflect.get(cache, prop).bind(cache);
这将解决该问题。
其他备注
对方法的访问将触发addAll
,这很遗憾,因为不确定此访问是否真的需要发生。例如,如果我们只是做了 const f = myproxy.has
,那么实际上没有必要贪婪地消耗可迭代对象。
使用代理模式,您必须返回一个替换函数才能进行此类方法访问,并且只能在该返回函数中调用 addAll
。我建议使用继承模式而不是代理模式。这似乎更适合实现所需的行为。
对于 has
方法,我们甚至可以考虑部分消耗可迭代对象,直到找到目标元素为止,因为任何进一步的消耗该 iterable 永远不会改变此方法调用的结果。
实现
这是您可以考虑的另一种“子类化”实现:
class LazySet extends Set {
#iterator
constructor(iterable) {
super();
this.#iterator = iterable[Symbol.iterator]();
this.#iterator.return = () => ({}); // Disable undesired return behaviour
}
* values() {
yield* super.values();
if (!this.#iterator) return;
let size = super.size;
for (const value of this.#iterator) {
super.add(value); // lazy
if (size === super.size) continue; // duplicate
yield value;
size++;
}
this.#iterator = null; // release reference
}
* entries() {
for (const value of this.values()) yield [value, value];
}
* keys() {
yield* this.values();
}
* [Symbol.iterator]() {
yield* this.values();
}
forEach(cb, thisArg) {
for (const value of this.values()) {
cb.call(thisArg, value, this);
}
}
has(needle) {
if (super.has(needle)) return true;
if (!this.#iterator) return false;
for (const value of this.#iterator) {
super.add(value); // lazy
if (value === needle) return true;
}
this.#iterator = null;
return false;
}
delete(needle) {
for (const _ of this.values()) {}; // consume iterator
return super.delete(needle);
}
get size() {
for (const _ of this.values()) {}; // consume iterator
return super.size;
}
}
// Demo
function* generator() {
for (let i = 0; i < 10; i++) {
console.log(` (about to yield ${i})`);
yield i;
}
}
let set = new LazySet(generator());
console.log("Does set have 3?:");
console.log(set.has(3));
console.log("-----------------");
console.log("first 5 values in set:");
let i = 0;
for (const value of set) {
console.log(value);
if (++i === 5) break;
}
console.log("-----------------");
console.log("All values in set:");
console.log(...set);
console.log("size:", set.size);
关于javascript - 如何制作懒惰集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74426639/