编辑
您的建议正在实施中。有一个 proposal at stage 2 of the TC39 process 用于向迭代器原型添加一大堆辅助方法(因此它们可以被集合使用),并且该提案包括您提到的两个 .forEach() 和 .reduce() 以及其他十几个。
我还没有完全理解这应该如何工作,因为规范谈到了迭代器助手,但随后显示在实际的 Set 实例上直接使用 .reduce(),就像在数组上使用它一样。所以,也许每个类都使用助手来实现他们自己的同名方法。由于您通常希望减少集合,而不是减少迭代器,因此这是有道理的。迭代器只是一个用于减少集合的工具,而不是集合本身。
他们redefine the .reduce() callback 只传递累加器和值(没有索引,没有对象)。仅供参考,我通过查看https://node.green/ 的末尾发现了这一点。因此,它正在开发中,由于有一个提议的标准,你可以填充它,你可以找到大量提议的新迭代器方法的示例实现here。
这是提议的 Set.prototype.reduce() 和 Map.prototype.reduce() 的 polyfill:
(function() {
if (!Set.prototype.reduce) {
Object.defineProperty(Set.prototype, "reduce", {value: reduce});
}
if (!Map.prototype.reduce) {
Object.defineProperty(Map.prototype, "reduce", {value: reduce});
}
function reduce(fn, initialValue) {
if (typeof fn !== "function") {
throw new TypeError("2nd argument to reduce must be function");
}
let noInitial = arguments.length < 2;
let accumulator = initialValue;
for (let [key, value] of this.entries()) {
// if no initial value, get it from the first value
if (noInitial) {
accumulator = value;
noInitial = false;
} else {
accumulator = fn(accumulator, key, value);
}
}
// if there was nothing to iterate and initialValue was not passed
// spec says this should be a TypeError
if (noInitial) {
throw new TypeError("iterable was empty and initalValue not passed")
}
return accumulator;
}
})();
// demo code
let s = new Set([1,2,3,4,5,6]);
let sum = s.reduce((total, val) => {
return total += val;
}, 0);
console.log(`Set Total = ${sum}`);
let m = new Map([['one',1],['two',2],['three',3],['four',4]]);
let sum2 = m.reduce((total, key, val) => {
return total += val;
}, 0);
console.log(`Map Total = ${sum2}`);
我还没有完全弄清楚基于Iterator 类的.reduce() 方法是如何自动实现的,以便set.reduce() 或map.reduce() 将“正常工作”。我不确定它确实如此。我在想每个类仍然需要连接它自己的 .reduce() 方法,但它可以使用 Iterator 对象上的帮助器实现来做到这一点。也许这就是他们被称为“帮手”的原因。它们只是可用于连接您自己的顶级方法的常用函数。
它们可能仍然可以在迭代器上直接访问,但这似乎不是您通常使用它们的方式。
原答案...
您实际上并不需要forEach(),因为您可以在任何可迭代对象上使用for/of。所以,如果你真的想要forEach(),你必须自己实现它。我不会称它为 polyfill,因为没有您要填写的标准。因此,最好把它做成一个独立的函数,不要以非标准的方式污染原型。
如果您只是尝试迭代并从迭代中收集一些单个值,那么肯定有一些论据支持使用类似 reduce() 的函数与可迭代对象一起工作。同样,由于没有适用于所有可迭代对象的标准实现,因此您必须实现自己的函数以适用于任何可迭代对象。
为任意迭代实现reduce() 的一个问题是Array.prototype.reduce() 将index 传递给回调。这有点假设index 可以像数组一样访问。但是,一些具有可迭代性的集合不能通过索引访问。您仍然可以在迭代期间创建一个索引并将其作为一个计数器传递给回调,但它不一定能像在执行someArray.reduce() 时那样使用索引。
这是reduce() 的实现,适用于任何可迭代对象。作为参考,这里是 the spec for Array.prototype.reduce(),它适用于索引访问,而不适用于可迭代,这就是为什么它不能直接用于任何可迭代,但可以用于任何类似数组的对象。
let s = new Set([1,2,3,4,5,6]);
function reduce(iterable, fn, initialValue) {
if (typeof fn !== "function") {
throw new TypeError("2nd argument to reduce must be function");
}
let initialValuePresent = arguments.length >= 3;
let accumulator = initialValue;
let cntr= 0;
for (let item of iterable) {
// if no initial value, get it from the first value
if (cntr === 0 && !initialValuePresent) {
accumulator = item;
} else {
accumulator = fn(accumulator, item, cntr, iterable);
}
++cntr;
}
// if there was nothing to iterate and initialValue was not passed
// spec says this should be a TypeError
if (cntr === 0 && !initialValuePresent) {
throw new TypeError("iterable was empty and initalValue not passed")
}
return accumulator;
}
let sum = reduce(s, (total, item, cntr, obj) => {
return total += item;
}, 0);
console.log(`Total = ${sum}`);