为了账号安全,请及时绑定邮箱和手机立即绑定

JavaScript 无法迭代 Map 键或值

JavaScript 无法迭代 Map 键或值

忽然笑 2023-07-14 15:33:42
在某些情况下,尝试迭代 JavaScript Map 键或值会失败。该脚本根本不进入循环。function pushAndLog(array, values) {    array.push(...values);    for (const value of values) {        console.log(value);    }}const array = [];const map = new Map([["a", 1], ["b", 2]]);pushAndLog(array, map.keys());在此示例中,array已正确更新,但地图键未记录。
查看完整描述

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 对象,从而放大了这个问题,出于同样的原因,这是错误的。



查看完整回答
反对 回复 2023-07-14
?
白衣非少年

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;

            }

        }

    };

}


查看完整回答
反对 回复 2023-07-14
  • 2 回答
  • 0 关注
  • 123 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信