简体   繁体   English

javascript的Set和常规普通对象之间有什么区别?

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

In trying to solve this question , I learned about the existence of javascript's Set object. 在尝试解决此问题时 ,我了解了javascript的Set对象的存在。 I then checked MDN's Set documentation , and the question I am asking here immediately popped up. 然后,我检查了MDN的Set文档 ,然后立即弹出我在这里提出的问题。

This debate started in the aforementioned question's answers comments. 这场辩论始于上述问题的答案评论。 I found that this thread also contributes to the debate. 我发现线索也有助于辩论。

So far, what I myself could make out of the debate is: 到目前为止,我本人可以从辩论中得出的结论是:

  • Set provides a "clear" method in its API, which is a nice-to-have feature Set在其API中提供了“清除”方法,这是一项很好的功能

  • Set ensures the order of the elements added is kept. Set确保添加的元素的顺序得以保留。 A plain object does not. 普通对象没有。

Can anyone provide a definite conclusion, if any? 任何人都可以提供明确的结论吗? Perhaps I should not be comparing the two... but I still think it is a legitimate question and that it is pretty likely that others will ask the same upon learning about Set. 也许我不应该将两者进行比较...但是我仍然认为这是一个合理的问题,其他人很可能在了解Set后会问同样的问题。

In any case, what initially propelled me to ask this was to edit MDN's Set specs adding the conclusion obtained here. 无论如何,最初促使我问这个问题的是编辑 MDN的Set规范,并添加从此处获得的结论。 But after the development of the thread here I think I should not be doing it, and perhaps Set's specs should not indeed mention a thing about "what about plain objects ultimately providing similar functionality". 但是在这里开发完线程之后,我认为我不应该这样做,也许Set的规范实际上不应提及“最终提供相似功能的普通对象该如何处理”。 At least whoever asks himself the question may still find it here in SO and enjoy the insights/contributions added here. 至少有人问自己这个问题,仍然可以在SO中找到它,并享受这里添加的见解/贡献。

First off, here's a post with code that shows how to start to use a plain Javascript object for set-like behavior (with some limitations). 首先, 这是一篇包含代码的文章 ,该文章显示了如何开始将纯Javascript对象用于类似集合的行为(有一些限制)。

And, here's a set of objects (that work in ES5) for getting lots more set-like behavior. 并且,这是一组对象 (在ES5中可用),用于获得更多类似集合的行为。

And, here a partial ES6 polyfill for the Set object implemented in ES5. 并且,这里是ES5中实现的Set对象的部分ES6 polyfill

If you study any of this code, you will see that a plain Javascript object falls far short of an ES6 Set in many ways that require significant additional coding to work around. 如果学习这些代码,您会发现普通的Javascript对象在很多方面都远不及ES6集,而这需要大量额外的代码来解决。


Here are a couple of those issues: 这里有几个问题:

  1. Assigning an object as a key will not work properly because all objects convert to a string key of "[object Object]" so all objects would have the same key in your set-like Javascript object. 将对象分配为键将无法正常工作,因为所有对象都将转换为字符串"[object Object]"因此所有对象在类似集合的Javascript对象中都具有相同的键。

  2. Assigning a number as a key will convert to the string representation so keys like 4 and "4" will conflict. 将数字分配为键将转换为字符串表示形式,因此键4"4"将发生冲突。

The ES6 Set object does not have these limitations. ES6 Set对象没有这些限制。 Here's more discussion of these two issues: 这是关于这两个问题的更多讨论:

If you look at this answer to a prior post and the code in that answer , you can see how an underlying Javascript object can be used as the lookup mechanism for set-like behavior. 如果您看一下先前文章的答案以及该答案中的代码 ,则可以看到如何将底层Javascript对象用作类似集合的行为的查找机制。 Without lots of other coding though, it has some limitations because a Javascript object requires a string as the lookup key. 尽管没有很多其他编码,但是它有一些局限性,因为Javascript对象需要使用字符串作为查找键。 An ES6 Set does not . ES6集没有 So, out of the box the ES6 Set supports objects as elements. 因此,开箱即用的ES6集支持将对象作为元素。 Using a Javascript object as a poor man's set does not support objects as elements in the set. 将Javascript对象用作穷人的集合不支持将对象作为集合中的元素。

This becomes the most material when you want to add an object to the plain Javascript object-based set. 当您要将对象添加到基于Javascript的普通对象集中时,这变得最重要。

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

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

Because a Javascript object requires a string key, it will call myObj.toString() to get such a key. 因为Javascript对象需要一个字符串键,所以它将调用myObj.toString()来获得这样的键。 Without a custom override for the toString() method, that will come out as "[object Object]" which is not at all what you want. 如果没有toString()方法的自定义覆盖,它将以“ [object Object]”的形式出现,这根本不是您想要的。 See here for a demo. 请参阅此处的演示。 It will appear to work for one object, but as soon as you have more than one object in the set or set the set for a different object, it won't work at all because all objects would get indexed with the same key. 它似乎适用于一个对象,但是一旦集合中有多个对象或为另一个对象设置了集合,则它将根本不起作用,因为所有对象都将使用相同的键进行索引。

With an actual ES6 set, on the other hand, it natively accepts and works with objects just fine - you don't have to do anything special. 另一方面,有了一个实际的ES6集,它就可以自然地接受并与对象一起工作-您不必做任何特殊的事情。

If you want to see how you can mimic an ES6 set as closely as possible using a plain Javascript object as the lookup mechanism, please read this answer . 如果您想了解如何使用普通Javascript对象作为查找机制来尽可能接近地模拟ES6集,请阅读此答案 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 . 更多信息位于github上 ,您可以其中看到要使常规的基于Javascript对象的集实现支持此ObjectSet实现的 Java对象的操作。 There's even an ES6 Set polyfill here that uses an underlying Javacript object as it's storage mechanism. 这里甚至还有一个ES6 Set polyfill 它使用底层Javacript对象作为其存储机制。


A second issue arises with Javascript object based set implementations which is also because of the string key requirement. 第二个问题是基于Javascript对象的集合实现,这也是由于字符串键的要求。 If you do this: 如果您这样做:

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

You will end up with only one item in the set. 您将最终只得到其中一个项目。 This is because mySet[4] = true; 这是因为mySet[4] = true; converts the 4 to a string "4" to use as the key. 4转换为字符串"4"以用作键。 If you are only using strings in your set or only using numbers in the set, then this can be easily worked around, but if you have both strings and numbers in the set, then a javascript object-base set implementation does not distinguish between 4 and "4" so does not treat them as separate items in the set. 如果您仅在集合中使用字符串或仅在集合中使用数字,则可以轻松解决此问题,但是如果集合中同时包含字符串和数字,则javascript对象库集合实现无法区分4"4"因此不会将它们视为集合中的单独项目。 An ES6 does make this distinction. ES6确实做到了这一点。

Again, it would be possible to work around this with more code. 同样,有可能使用更多代码来解决此问题。 If you manually do the toString() conversion yourself to create the key, one can preprend to the key value some type information such that in the above example, the key for the number 4 becomes "num_4" instead of just "4". 如果您手动进行toString()转换以创建密钥,则可以在密钥值前添加一些类型信息,以便在上面的示例中,数字4的密钥变为"num_4"而不仅仅是“ 4”。 To prevent collisions with a string '"num_4"`, you have to also do the same for string types. 为了防止与字符串““ num_4”`发生冲突,您还必须对字符串类型执行相同的操作。 And numbers aren't the only type with this issue. 而数字并不是这个问题的唯一类型。 In fact, in this ES6 Set polyfill, you can see a unique key being generated here . 实际上,在此ES6 Set polyfill中,您可以在此处看到正在生成的唯一密钥。 That unique key then has to be regenerated and used to do any lookup in the set. 然后,必须重新生成该唯一密钥,并将其用于集合中的任何查找。

Sets don't take duplicates see this 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 instances are objects. 集合实例是对象。 If you construct a Set, you're free to add properties to it just like an instance of Date or Array: 如果构造一个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!

The behaviors exposed by the Set prototype provide a functionality that really has nothing to do with the nature of objects, other than that references to objects are among the sorts of values that can be used with that API. Set原型公开的行为提供的功能实际上与对象的性质无关,除了对对象的引用属于可以与该API一起使用的各种值。 Adding or removing values from a set via that API has no effect on the ordinary properties of the set object. 通过该API在集合中添加或删除值对集合对象的普通属性没有影响。

If one wished to implement a facility like the Set facility using only ES5 facilities, you'd have to do something like maintain an array of values, or a set of arrays by value type perhaps. 如果希望只使用ES5工具来实现Set工具之类的工具,则您必须执行一些操作,例如维护值数组或按值类型排列一组数组。 The .add() and .has() and other methods would have to impose the rules of set membership over that list of values, probably by searching. .add().has()等方法可能必须通过搜索在该值列表上强加集合成员资格的规则。 There are probably some optimizations that could be done to make the searching more efficient, but the ES6 runtime has advantages, most notably that the runtime internals have access to raw object reference values and other such things. 可能可以进行一些优化以提高搜索效率,但是ES6运行时具有优势,最显着的是运行时内部可以访问原始对象引用值和其他类似内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM