ES6 中的地图与对象,何时使用?

Posted

技术标签:

【中文标题】ES6 中的地图与对象,何时使用?【英文标题】:Maps vs Objects in ES6, When to use? 【发布时间】:2015-12-12 13:00:09 【问题描述】:

Ref: MDN Maps

当键在运行时之前是未知的,以及何时在对象上使用映射 所有的键都是相同的类型,所有的值都是相同的类型。

当存在对单个元素进行操作的逻辑时使用对象。

问题:

在对象上使用地图的适用示例是什么?特别是“什么时候密钥在运行时才未知?”

var myMap = new Map();

var keyObj = ,
    keyFunc = function ()  return 'hey',
    keyString = "a string";

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");

console.log(myMap.get(keyFunc));

【问题讨论】:

是的,我注意到了。当我将函数设置为值时。 @JonathanLonowski 你能想到我什么时候应该这样做 :( 很难考虑用例。 您可以在您说要使用 Object 关联一些数据的 DOM 元素的地方使用它。除了使用元素的 ID 作为对象中的键之外,您还可以将元素本身用作 Map 中的键,这样您就不必关心元素是否具有 ID(或除对象引用之外的任何其他唯一标识符) 与否。 @RobG 只是一点点补充:在这种情况下,WeakMap 也可能会有所帮助。 我认为这建议将对象用作/用于记录,将地图用于所有其他类型的映射。对于记录,我指的是具有一组固定字段的数据结构,例如具有字段 nameid 的用户对象。 当我阅读 MDN 页面时,用例的要点列表比您引用的段落更有帮助。当然与您标题中提出的问题有关。 【参考方案1】:

在对象上使用地图的适用示例是什么?

我想你已经给出了一个很好的例子:当你使用对象(包括函数对象)作为键时,你至少需要使用Maps。

尤其是“在运行时之前什么时候密钥是未知的?”

当它们在编译时未知时。简而言之,当您需要key-value collection 时,您应该始终使用Map。需要集合的一个很好的指标是当您从集合中动态添加和删除值时,尤其是当您事先不知道这些值时(例如,它们是从数据库中读取的,由用户输入等)。

相反,当您在编写代码时知道对象具有哪些属性和多少属性时,您应该使用对象——当它们的形状是静态的时。正如@Felix 所说:当您需要record 时。需要的一个很好的指标是当字段具有不同的类型时,并且当您永远不需要使用括号表示法(或期望其中包含一组有限的属性名称)时。

【讨论】:

或者从另一个角度来看:每当您需要在数据级别(例如for..of)而不是程序级别(例如for..in)迭代对象的属性时,请使用Map .有关此条款的更多信息in this response。 我还将添加一个注释,即无论何时您不知道您的密钥将是哪种类型并且您不希望字符串作为密钥数据类型,然后使用 map ***.com/questions/32600157/… 【参考方案2】:

当键在运行时之前未知,并且所有键的类型相同且所有值的类型相同时,对对象使用映射。

我不知道为什么有人会写出如此明显错误的东西。我不得不说,现在人们在 MDN 上发现越来越多的错误和/或有问题的内容。

那句话中的任何内容都不正确。使用映射的主要原因是当您需要对象值键时。值应该是相同类型的想法是荒谬的——尽管它们可能是,当然。在运行时之前不知道键的情况下不应该使用对象的想法同样荒谬。

【讨论】:

我不明白这个想法有什么荒谬之处?当您需要集合时,它们通常是严格类型的(当然也可能有例外)。另外,我认为不要再将对象用于集合(当Map 可用时)是个好建议。 集合通常是严格类型化的,这与说严格类型化的东西应该是一个集合在逻辑上是不同的。 是的,我认为这有点奇怪,但这个想法很好。我猜他们认为“在运行时密钥是未知的”已经收集了。有更好的措辞的想法吗? 同意。 MDN 有很好的文档,但他们应该坚持记录 API 文档,而不是试图提供编程建议。【参考方案3】:

我认为使用 ES2015 的 Map 只剩下两个理由来使用普通对象:

您根本不想迭代对象类型的属性 或者你这样做,但属性顺序无关紧要,你可以在迭代时distinguish the program from the data level

什么时候属性顺序不重要了?

如果您只有一个值和一些应该与其显式关联的函数(例如 Promise - 它是未来值的代理 - 和 then/catch) 如果您有一个类似结构/记录的数据结构,其中包含一组在“编译时”已知的静态属性(通常结构/记录不可迭代)

在所有其他情况下,您可能会考虑使用Map,因为它保留了属性顺序并将程序(分配给Map 对象的所有属性)与数据级别(Map 本身中的所有条目)分开。

Map的缺点是什么?

你失去了简洁的对象文字语法 您需要 JSON.stringyfy 的自定义替换器 你失去了解构,这对静态数据结构更有用

【讨论】:

这一行让我非常关心:“它可能比​​纯的、类似 hashmap 的对象要慢”,因为性能改进,我想用映射替换我的所有对象。但是你说它比较慢... 你是对的。 Map 可能更快,因为它纯粹基于散列,而Object 稍微复杂一些。谢谢!【参考方案4】:

这个问题与 重复,但在它关闭之前,这里是my answer from over there:

除了其他答案之外,我发现地图比对象更笨拙和冗长。

obj[key] += x
// vs.
map.set(map.get(key) + x)

这很重要,因为更短的代码阅读速度更快,表达更直接,更好kept in the programmer's head。

另一方面:因为 set() 返回的是映射,而不是值,所以不可能链式赋值。

foo = obj[key] = x;  // Does what you expect
foo = map.set(key, x)  // foo !== x; foo === map

调试地图也更痛苦。在下面,您实际上看不到地图中的键。你必须编写代码才能做到这一点。

任何 IDE 都可以评估对象:

【讨论】:

这根本没有回答“在对象上使用地图的适用示例是什么?”这个问题 请不要交叉发布您的答案。不,这个问题不是重复的。【参考方案5】:

MapObject 之间的区别之一是:

Map 可以使用复杂数据类型作为其键。像这样:

const fn = function() 
const m = new Map([[document.body, '***'], [fn, 'redis']]);

m.get(document.body) // '***'
m.get(fn) //'redis'

注意:对于复杂的数据类型,如果要获取值,必须传递与key相同的引用。

Object,它只接受简单数据类型(numberstring)作为它的key。

const a = ;
a[document.body] = '***';

console.log(a) //[object htmlBodyElement]: "***"

【讨论】:

【参考方案6】:

Objects 与Maps 相似,两者都允许您将键设置为值、检索这些值、删除键以及检测键中是否存储了某些内容。正因为如此(并且因为没有内置的替代品),Objects 在历史上一直被用作Maps;但是,在某些情况下使用Map 更可取:

Object 的键是 Strings 和 Symbols,而它们可以是 Map 的任何值,包括函数、对象和任何原语。 Map 中的键是有序的,而添加到对象的键不是。因此, 迭代它时,Map 对象按顺序返回键 插入。 您可以使用size 属性轻松获取Map 的大小,同时 Object 中的属性数量必须手动确定。 Map 是可迭代的,因此可以直接迭代,而 遍历Object 需要以某种方式获取其密钥 并对其进行迭代。 Object 有一个原型,所以映射中有默认键 如果您不小心,可能会与您的钥匙发生碰撞。从 ES5 开始 可以使用map = Object.create(null) 绕过,但这是 很少做。 Map 在涉及频繁添加和 删除密钥对。

MDN

【讨论】:

以上是关于ES6 中的地图与对象,何时使用?的主要内容,如果未能解决你的问题,请参考以下文章

确定 MKRoutestep 何时完成

何时使用静态类与实例化类

ES6 中的解构赋值与对象属性访问

何时使用向量中的对象以及何时使用指向向量中对象的指针? [关闭]

ES6 简写:

ES6中的Map集合(与java里类似)