javascript 的 Set 和普通的普通对象有什么区别?

What are the differences between javascript's Set and a regular plain object?

在尝试解决此问题时 , I learned about the existence of javascript's Set object. I then checked MDN's Set documentation,我在这里提出的问题立即出现。

在上述 answers comments. I found that this 话题中发起的这场辩论也对辩论有所贡献。

到目前为止,我自己可以从辩论中得出的结论是:

谁能给出一个确定的结论,如果有的话?也许我不应该将两者进行比较...但我仍然认为这是一个合理的问题,并且其他人在学习 Set 后很可能会问同样的问题。

无论如何,最初促使我提出这个问题的是 编辑 MDN 的设置规范,添加此处获得的结论。但是在这里开发线程之后,我认为我不应该这样做,也许 Set 的规范确实不应该提及 "what about plain objects ultimately providing similar functionality"。至少问自己这个问题的人仍然可以在 SO 中找到它并享受这里添加的 insights/contributions。

首先,here's a post 代码展示了如何开始使用普通 Javascript 对象来实现类似集合的行为(有一些限制)。

而且,这里是 a set of objects(在 ES5 中工作)以获得更多类似集合的行为。

并且,这里是 ES5 中实现的 Set 对象的部分 ES6 polyfill

如果你研究任何这段代码,你会发现一个普通的 Javascript 对象在很多方面都远不及 ES6 Set,需要大量的额外编码才能解决。


以下是其中的几个问题:

  1. 将对象指定为键将无法正常工作,因为所有对象都会转换为 "[object Object]" 的字符串键,因此所有对象在您的集合中都将具有相同的键,例如 Javascript对象.

  2. 将数字指定为键将转换为字符串表示形式,因此 4"4" 等键将发生冲突。

ES6 Set 对象没有这些限制。下面是对这两个问题的更多讨论:

如果您查看 this answer to a prior post and the code in that answer,您可以了解如何将底层 Javascript 对象用作类集合行为的查找机制。虽然没有很多其他编码,但它有一些限制,因为 a Javascript 对象需要一个字符串作为查找键。 ES6 集不会。因此,开箱即用的 ES6 Set 支持将对象作为元素。使用 Javascript 对象作为穷人集合不支持对象作为集合中的元素。

当您想将对象添加到普通的 Javascript 基于对象的集合时,这成为最 material。

// create a set-like object
var mySet = {};

// create an object and put it in the set
var myObj = {greeting: "hello"};
mySet[myObj] = true;

因为一个Javascript对象需要一个字符串键,它会调用myObj.toString()来得到这样一个键。如果没有对 toString() 方法的自定义覆盖,那将以“[object Object]”的形式出现,这根本不是您想要的。有关演示,请参阅 here。它似乎适用于一个对象,但一旦集合中有多个对象或为不同的对象设置集合,它就根本不起作用,因为所有对象都将使用相同的键进行索引。

另一方面,对于实际的 ES6 集,它本身就可以很好地接受和处理对象——你不需要做任何特殊的事情。

如果您想了解如何使用普通 Javascript 对象作为查找机制尽可能地模仿 ES6 集,请阅读 this answer. Further info is located on github where you can see what as to be done to make a regular Javascript object-based set implementation support Javascript objects with this ObjectSet implementation. There's even an ES6 Set polyfill here,它使用底层 Javacript 对象存储机制。


第二个问题 出现在 Javascript 基于对象的集合实现中,这也是由于字符串键要求。如果你这样做:

var mySet = {};
mySet[4] = true;
mySet["4"] = true;

您最终只会得到套装中的一件物品。这是因为 mySet[4] = true;4 转换为字符串 "4" 以用作键。如果您仅在集合中使用字符串或仅在集合中使用数字,那么这很容易解决,但如果集合中同时包含字符串和数字,则 javascript 基于对象的集合实现可以不区分 4"4" 因此不将它们视为集合中的单独项目。 ES6 确实做出了这种区分。

同样,可以使用更多代码解决此问题。如果您自己手动进行 toString() 转换以创建键,则可以在键值中预先添加一些类型信息,例如在上面的示例中,数字 4 的键变为 "num_4"而不仅仅是“4”。为了防止与字符串 '"num_4"` 发生冲突,您还必须对字符串类型执行相同的操作。数字并不是唯一存在此问题的类型。事实上,在这个 ES6 Set polyfill 中,您可以看到正在生成一个唯一的键 here。然后必须重新生成该唯一键并用于在集合中进行任何查找。

集不要重复看到这个fiddle

var mySet = new Set();

mySet.add(1);
mySet.add(5);
mySet.add("some text");


console.log('after adding duplicate  values ')
console.log('size='+mySet.size ) // size is 3


mySet.add("some text"); // adding duplicate value
console.log('after adding duplucate  value ')
console.log('size='+mySet.size ) // size is still 3

集合实例是对象。如果你构造一个 Set,你可以像 Date 或 Array 的实例一样自由地向它添加属性:

var s = new Set();
s.name = "Herman";
s.shoeSize = 12;
console.dir(s); // just like an Object!
s.add(17);
if (s.has(17)) alert("yup!"); // yup!
if (17 in s) alert("yup!"); // no alert here!

Set 原型公开的 行为 提供的功能实际上与对象的性质无关,只是对对象的引用属于可以与 API 一起使用。通过 API 从集合中添加或删除值对集合对象的普通属性没有影响。

如果希望仅使用 ES5 工具来实现类似 Set 工具的工具,则您必须执行一些操作,例如维护一个值数组,或者可能按值类型维护一组数组。 .add().has() 以及其他方法可能必须通过搜索将集合成员资格规则强加于该值列表。可能有一些优化可以使搜索更有效,但 ES6 运行时有优势,最显着的是运行时内部可以访问原始对象引用值和其他类似的东西。