问题很简单:有什么方法可以调用filter
、find
、map
等数组方法吗?不仅在数组上,而且在任何可迭代对象上?
filter、find、map 等不仅对数组有意义,而且通常对序列有意义。 iterable 是一个可以被迭代的序列,因此过滤序列、查找(序列中的第一个元素)、映射序列的元素......无论序列是什么都是有意义的。
想象一下这样的情况:一个无限生成器(例如斐波那契数列,生成器一次返回一个项目)。我想找到满足给定条件的第一个元素。像这样使用传播:
[...fib()].find(conditionFunction)
会先把 fib 序列转储出来,这会导致浏览器因为内存消耗而崩溃(无限序列)。我能做的是手动调用 for 循环并在其中使用 conditionFunction。
有没有什么方法可以在(非数组)可迭代对象上延迟调用过滤器、查找、映射等?
不幸的是,像find 这样的迭代器方法是使用序列协议(protocol)(.length
+ Get
)实现的,而不是迭代器协议(protocol)。你可以尝试用一个代理来愚弄他们,让可迭代对象模拟序列,例如
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])