ES6 Set 允许重复的数组/对象

17
请查看下面的脚本。我正在使用Chrome进行测试。
/*declare a new set*/
var items = new Set()

/*add an array by declaring as array type*/
var arr = [1,2,3,4];
items.add(arr);

/*print items*/
console.log(items); // Set {[1, 2, 3, 4]}

/*add an array directly as argument*/
items.add([5,6,7,8]);

/*print items*/
console.log(items); // Set {[1, 2, 3, 4], [5, 6, 7, 8]}

/*print type of items stored in Set*/
for (let item of items) console.log(typeof item); //object, object

/*check if item has array we declared as array type*/
console.log(items.has(arr)); // true

/*Now, check if item has array we added through arguments*/
console.log(items.has([5,6,7,8])); //false

/*Now, add same array again via argument*/
items.add([1,2,3,4]);

/*Set has duplicate items*/
console.log(items); // Set {[1, 2, 3, 4], [5, 6, 7, 8], [1, 2, 3, 4]}
  1. 为什么在items.has([5,6,7,8])处返回false?
  2. 为什么允许重复的值?我认为“集合是一个有序的值列表,不能包含重复项”
  3. 如何访问由items.add([5,6,7,8])添加的数组?

1
[5, 6, 7, 8] !== [5, 6, 7, 8] 这段代码也返回 false,每个数组都是一个独立的对象。 - 4esn0k
“我曾认为集合是有序值的列表”,但实际上,按定义来看,列表是有序的,而集合通常不是有序的。 - a better oliver
2
@4esn0k 那个表达式返回 true - xyres
1
@xyres 看起来是个打字错误。他的意思是,如果你比较两个具有相同值的不同数组,结果总是会为false,因为它们是两个不同的数组实例,即使它们的内容相同。 - Antheus_S
3个回答

22
  1. 为什么在 items.has([5,6,7,8]) 处返回 false?

    来自 MDN

    Set 对象允许你存储任何类型的唯一的,无论是基本类型值或者是对象引用

    对象是使用引用而非值进行比较。Set 使用SameValueZero(x, y) 比较算法来比较值。它会返回 如果 x 和 y 是相同的对象值,则返回 true。否则,返回 false

  2. 为什么它允许重复值?我认为“集合是一个有序的值列表,不能包含重复项”

    与问题1相同。如果已经向集合中添加了相同的对象(不仅仅是看起来相同),则称非基本值已存在于集合中。

  3. 如何访问通过 items.add([5,6,7,8]) 添加的数组?

    您需要创建一个变量并将其添加到集合中。然后可以使用此变量来检查集合中是否有该数组。


谢谢,我想表达的是,如果Set允许添加项目,即使它们没有引用集并且可能永远不会被访问,那么为什么还要提供将这样的项目添加到Set中的规定,这些项目可能永远不会被访问而且在这种情况下,我们没有引用。我并没有偏离原始问题,只是想知道也许有人有理由证明Set中没有引用的项目。 - jeetaz
1
@jeetaz 因为你可以在集合中添加任何内容,然后使用 for..of 进行访问。这样做是为了在需要迭代时使添加元素的过程更加灵活。 - Tushar
在这种情况下,它只是一个对象数组,可以像传统数组一样进行迭代。但也说得过去! - jeetaz

4

引用 规范

Set 对象是 ECMAScript 语言值的集合。一个不同的值只能作为 Set 集合的元素出现一次。使用 SameValueZero 比较算法区分不同的值。

(强调是我的)

SameValueZero 比较算法 处理任何两个相同类型的参数(类型不是 nullundefined、字符串、数字、布尔或符号)如下:

如果 x 和 y 是相同的对象值,则返回 true。否则,返回 false。

最终,这意味着对象是通过引用进行比较的(也就是说,“这两个对象指向内存中的同一位置吗?”)。每次使用方括号([])或构造函数调用(new Array())创建数组时,都会在内存中创建一个新的Array实例。当您保留对数组的引用时,它将匹配(例如,arr === arr)。当您创建新实例并进行比较时,即使它们的内容相等,它们也不会匹配(例如,[1, 2, 3] !== [1, 2, 3])。


我明白你所说的按引用比较,但在这种情况下,添加到集合中的项目是无用的,因为我们没有引用。为什么允许添加这样的项目而不是忽略它呢?我的意思是,我不明白为什么要在Set中拥有存在但永远无法访问的项目,因为我们没有引用进行比较。 - jeetaz
因为以后您可以使用引用 - 例如 var nowIhaveReference = Array.from(items)[2] - Sean Vieira
但是我们不也可以用对象数组来做这件事吗? - jeetaz

2
  1. 数组比较不是比较值而是比较引用,所以它会始终返回false。例如:[1] === [1]
  2. 请参考MDN

Set对象允许您存储任何类型的唯一值,无论是原始值还是对象引用。

您正在传递新对象而不是引用,所以它允许添加重复项。这些重复项在视觉上看起来相似但引用却不同。

  1. 通过将引用分配给变量来访问已添加的数组。

4
"你不能直接在Javascript中比较数组"这句话是很容易引起误解的。[1] == [2] 这个例子就是在"直接"比较两个数组。请注意,这样的比较可能会导致不确定的行为,因为它们比较的是对象引用而不是内容。 - zerkms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接