2 回答

TA貢獻1776條經(jīng)驗 獲得超12個贊
原因
發(fā)生這種情況是因為Map.keys()和Map.values()方法返回Iterator而不是Iterable對象。
怎么修
避免重復使用迭代器
與 Iterable 相反,Iterator 是一個有狀態(tài)的對象。你不能重復使用它。特別是,如果將迭代器傳遞給函數(shù),則之后不能使用它 - 您應該創(chuàng)建一個新的迭代器。但這并不總是可能的。
使用可迭代對象
修復此代碼的最簡單方法是將集合而不是迭代器傳遞給函數(shù):
pushAndLog(array,?[...map.keys()]);
但這種解決方案通常不是最有效的,因為創(chuàng)建新集合會消耗內(nèi)存和 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));
它對于更大的地圖效果更好。
筆記:
這些函數(shù)是用 TypeScript 編寫的。要將它們轉(zhuǎn)換為 JavaScript,只需從函數(shù)頭中刪除類型定義即可。
這些函數(shù)被編寫為 ES6 模塊。要將它們轉(zhuǎn)換為純 JavaScript,只需刪除
export
關(guān)鍵字即可。嵌套函數(shù)是生成器函數(shù),即它們返回一個生成器,該生成器也可以用作迭代器。為了使用此代碼,請確保您的解釋器支持生成器或?qū)?TypeScript/Babel 轉(zhuǎn)譯器與regenerator-runtime庫結(jié)合使用。
一些想法
從 Map.keys() 和 Map.values() 方法返回 Iterator 對象是一個糟糕的設計決策,我們必須忍受并始終牢記在心。此類錯誤有時是意料之外的,并且很難跟蹤,因為迭代器在您嘗試重用它們之前效果很好。所以你必須保持警惕。
在其他成熟的編程語言中,此類方法巧妙地返回可迭代對象:
在Java中,Map#keySet方法返回一個擴展了Iterable接口的Set;
在.NET中,Dictionary.Keys屬性是一個實現(xiàn)IEnumerable接口的KeyCollection對象。
Java Iterable 和 .NET IEnumerable 都是可重用的,因此它們的用戶不會遇到相同的問題。這種方法更加安全。對我來說,一個問題是為什么相對年輕的 JavaScript 社區(qū)會發(fā)明自己的解決方案,而不是繼承現(xiàn)有成熟編程語言的最佳實踐。
盡管 JavaScript Iterator 的工作方式或多或少類似于 Iterable 對象(您可以在 for..of 循環(huán)、擴展運算符、集合構(gòu)造函數(shù)等中使用它),但它正式打破了可重用的 Iterable 類契約,而 Iterator 是不是。
TypeScript 定義假裝 Map.keys() 和 Map.values() 返回一個擴展 Iterable 接口的所謂 IterableIterator 對象,從而放大了這個問題,出于同樣的原因,這是錯誤的。

TA貢獻1155條經(jīng)驗 獲得超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;
}
}
};
}
添加回答
舉報