2 回答
TA贡献1776条经验 获得超12个赞
原因
发生这种情况是因为Map.keys()和Map.values()方法返回Iterator而不是Iterable对象。
怎么修
避免重复使用迭代器
与 Iterable 相反,Iterator 是一个有状态的对象。你不能重复使用它。特别是,如果将迭代器传递给函数,则之后不能使用它 - 您应该创建一个新的迭代器。但这并不总是可能的。
使用可迭代对象
修复此代码的最简单方法是将集合而不是迭代器传递给函数:
pushAndLog(array, [...map.keys()]);
但这种解决方案通常不是最有效的,因为创建新集合会消耗内存和 CPU 时间。有更好的解决方案。将以下实用程序添加到您的代码中:
export function getIterableKeys<K, V>(map: Iterable<readonly [K, V]>): Iterable<K> {
return {
[Symbol.iterator]: function* () {
for (const [key, _] of map) {
yield key;
}
}
};
}
export function getIterableValues<K, V>(map: Iterable<readonly [K, V]>): Iterable<V> {
return {
[Symbol.iterator]: function* () {
for (const [_, value] of map) {
yield value;
}
}
};
}
使用它们代替原生 Map.keys() 和 Map.values() 方法:
pushAndLog(array, getIterableKeys(map));
它对于更大的地图效果更好。
笔记:
这些函数是用 TypeScript 编写的。要将它们转换为 JavaScript,只需从函数头中删除类型定义即可。
这些函数被编写为 ES6 模块。要将它们转换为纯 JavaScript,只需删除
export
关键字即可。嵌套函数是生成器函数,即它们返回一个生成器,该生成器也可以用作迭代器。为了使用此代码,请确保您的解释器支持生成器或将 TypeScript/Babel 转译器与regenerator-runtime库结合使用。
一些想法
从 Map.keys() 和 Map.values() 方法返回 Iterator 对象是一个糟糕的设计决策,我们必须忍受并始终牢记在心。此类错误有时是意料之外的,并且很难跟踪,因为迭代器在您尝试重用它们之前效果很好。所以你必须保持警惕。
在其他成熟的编程语言中,此类方法巧妙地返回可迭代对象:
在Java中,Map#keySet方法返回一个扩展了Iterable接口的Set;
在.NET中,Dictionary.Keys属性是一个实现IEnumerable接口的KeyCollection对象。
Java Iterable 和 .NET IEnumerable 都是可重用的,因此它们的用户不会遇到相同的问题。这种方法更加安全。对我来说,一个问题是为什么相对年轻的 JavaScript 社区会发明自己的解决方案,而不是继承现有成熟编程语言的最佳实践。
尽管 JavaScript Iterator 的工作方式或多或少类似于 Iterable 对象(您可以在 for..of 循环、扩展运算符、集合构造函数等中使用它),但它正式打破了可重用的 Iterable 类契约,而 Iterator 是不是。
TypeScript 定义假装 Map.keys() 和 Map.values() 返回一个扩展 Iterable 接口的所谓 IterableIterator 对象,从而放大了这个问题,出于同样的原因,这是错误的。
TA贡献1155条经验 获得超0个赞
惊人的解决方案!
// 1.获取Map的keys
export function getIterableKeys<K, V>(map: Iterable<readonly [K, V]>): Iterable<K> {
return {
[Symbol.iterator]: function* () {
for (const [key, _] of map) {
yield key;
}
}
};
}
// 2.获取key的values
export function getIterableValues<K, V>(map: Iterable<readonly [K, V]>): Iterable<V> {
return {
[Symbol.iterator]: function* () {
for (const [_, value] of map) {
yield value;
}
}
};
}
添加回答
举报