javascript' Set和常规普通对象有什么区别?

时间:2015-03-10 15:00:14

标签: javascript ecmascript-6 specifications

在尝试解决这个question时,我了解了javascript的Set对象的存在。然后我检查了MDN's Set documentation,我在这里问的问题立刻就出现了。

这场辩论始于前面提到的question's答案评论。我发现this线程也有助于辩论。

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

  • 设置提供"清除" API中的方法,这是一个很有用的功能

  • 设置确保保留添加的元素的顺序。普通对象不会。

任何人都可以提供明确的结论吗?也许我不应该比较两者......但我仍然认为这是一个合理的问题,很可能其他人在学习Set时会问同样的问题。

在任何情况下,最初促使我问这个问题的是编辑 MDN的Set规格,并在此处获得结论。但是在线程开发之后,我认为我不应该这样做,也许Set的规范确实不应该提及关于普通对象最终提供类似功能的事情"。至少问自己这个问题的人仍然可以在SO中找到它并享受这里添加的见解/贡献。

3 个答案:

答案 0 :(得分:1)

首先,here's a post代码显示如何开始使用简单的Javascript对象进行类似集合的行为(有一些限制)。

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

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

如果您研究了这些代码中的任何一个,您将看到一个简单的Javascript对象远远超过了ES6 Set,它需要大量额外的编码才能解决。


以下是其中的几个问题:

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

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

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

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

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

    // 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。它似乎适用于一个对象,但只要您在集合中有多个对象或为另一个对象设置该集合,它就根本不起作用,因为所有对象都将使用相同的键进行索引。 / p>

    另一方面,使用实际的ES6设置,它可以原生地接受和使用对象 - 你不需要做任何特别的事情。

    如果您想了解如何使用普通Javascript对象作为查找机制尽可能接近地模仿ES6集,请阅读this answer。更多信息是located on github,您可以在其中查看如何使常规基于Javascript对象的集合实现支持this ObjectSet implementation的Javascript对象。甚至还有一个ES6 Set polyfill here,它使用底层的Javacript对象作为它的存储机制。


    第二个问题出现了基于Javascript对象的集合实现,这也是因为字符串密钥要求。如果你这样做:

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

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

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

答案 1 :(得分:1)

设置不重复请参阅此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

答案 2 :(得分:1)

设置实例是对象。如果构造一个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添加或删除集合中的值不会影响set对象的普通属性。

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