问题很简单:有没有办法调用数组方法,如filter
,find
,map
等,不仅在数组上,而且在任何可迭代的吗
filter,find,map等不仅在数组上有意义,而且通常在序列上有意义。而iterable是一个可以被提升的序列,因此过滤序列,查找(序列中的第一个元素),映射序列的元素......无论序列是什么都是有意义的。
想象一下这样的情况:无限生成器(例如斐波那契序列,生成器一次返回一个项目)。我想找到满足给定条件的第一个元素。使用像这样的传播:
[...fib()].find(conditionFunction)
将首先使fib序列被转储,由于内存消耗(无限序列)导致浏览器崩溃。我能做的是手动调用循环并在里面使用conditionFunction。
有没有办法在(非数组)iterables上懒惰地调用filter,find,map等?
答案 0 :(得分:3)
不幸的是,像find这样的迭代器方法是使用序列协议(.length
+ Get
)实现的,而不是使用迭代器协议。您可以尝试使用代理来欺骗它们,这将使迭代模拟序列,例如
let asArray = iterable => new Proxy(iterable, {
get(target, prop, receiver) {
if(prop === 'length')
return Number.MAX_SAFE_INTEGER;
return target.next().value;
}
});
function *fib() {
let [a, b] = [1, 1];
while (1) {
yield b;
[a, b] = [b, a + b];
}
}
found = [].find.call(
asArray(fib()),
x => x > 500);
console.log(found);

需要更多的工作,但你明白了。
另一种(和IMO更清洁的方式)是重新实现迭代器方法以支持迭代(并成为生成器本身)。幸运的是,这非常简单:
function *lazyMap(iter, fn) {
for (let x of iter)
yield fn(x);
}
for (let x of lazyMap(fib(), x => x + ' hey'))...
以下是如何使用可链接方法创建一个惰性迭代器对象:
let iter = function (it) {
return new _iter(it);
};
let _iter = function(it) {
this.it = it;
};
_iter.prototype[Symbol.iterator] = function *() {
for (let x of this.it) {
yield x;
}
};
_iter.prototype.map = function (fn) {
let _it = this.it;
return iter((function *() {
for (let x of _it) {
yield fn(x)
}
})())
};
_iter.prototype.take = function (n) {
let _it = this.it;
return iter((function *() {
for (let x of _it) {
yield x;
if (!--n)
break;
}
})())
};
// @TODO: filter, find, takeWhile, dropWhile etc
// example:
// endless fibonacci generator
function *fib() {
let [a, b] = [1, 1];
while (1) {
yield b;
[a, b] = [b, a + b];
}
}
// get first 10 fibs, multiplied by 11
a = iter(fib())
.map(x => x * 11)
.take(10)
console.log([...a])