新的 JavaScript Set 方法即将推出!自 Firefox 127 起,这些方法已在大多数主流浏览器引擎中可用,这意味着您将无需 polyfill 来使其在所有地方都能工作。
本文适合刚接触 JavaScript Set 并想了解如何使用这些新 JavaScript 方法的人阅读。我将重点介绍使用这些方法的一些优点,并提供基本示例,说明为什么您可能需要使用它们而不是自己实现。
对于那些想要快速了解的人来说,以下是具有跨浏览器支持的新方法的要点:
如果您已阅读(或浏览)以上列表但感到困惑,请不用担心,我们将在后续章节中进行描述。这些方法都用于检查 Set 的内容与另一个特定 Set 的内容相比如何。
Set 类似于 Array,但每个值只能存储一次。例如,我们可以列出一系列项目,将它们全部添加到 Set 中,然后检查 Set 的结果。右侧的列表是左侧 <ol> 列表的内容,但已转换为 Set。我们已从列表中删除了所有重复项,因为我们保证 Set 中的元素是唯一的。
<div id="container">
<div>
<span>My list</span>
<ol>
<li>apple</li>
<li>pear</li>
<li>carrot</li>
<li>pear</li>
<li>apple</li>
<li>yoshi</li>
</ol>
</div>
<div>
<span>My set</span>
<ol id="set"></ol>
</div>
</div>
const allListItems = document.querySelectorAll("li");
const setList = document.getElementById("set");
// Create a new set and add list items
const mySet = new Set();
allListItems.forEach((item) => {
mySet.add(item.textContent);
});
// List the items in the set
mySet.forEach((item) => {
const li = document.createElement("li");
li.textContent = item;
setList.appendChild(li);
});
body {
font-family: monospace;
}
#container {
display: flex;
gap: 1rem;
justify-content: space-evenly;
}
在这样的小例子中,这可能看起来没什么大不了的,但拥有一个内置的方法来创建唯一的项目集合非常方便,尤其是在处理大量数据和更复杂的数据类型和对象时。例如,可以像这样向 Set 添加元素:
const dogs = new Set();
const yoshi = { name: "Yoshi", personality: "Friendly" };
dogs.add(yoshi);
通常,在 Set 中检查元素是否存在也比在 Array 中快,因此这适用于需要关注大型数据集性能的用例。您还可以基于现有 Set 组合具有特定逻辑属性的新 Set,我们将在下面看一些示例。
我们将在下面介绍所有新方法,但如果您想了解 Set 的所有功能,请查看 实例方法文档。
如果您不熟悉 Set,这是一个很好的入门示例。通过并集,我们可以检查“任一或两者” Set 中存在哪些元素。在下面的示例中,我们有两个列表,我们想创建一个第三个列表,其中包含两个列表中的所有项目,但没有重复项。
<div id="container">
<div>
<span>Set one</span>
<ol id="set-one">
<li>apple</li>
<li>banana</li>
<li>pear</li>
<li>carrot</li>
</ol>
</div>
<div>
<span>Set two</span>
<ol id="set-two">
<li>banana</li>
<li>peach</li>
<li>plum</li>
<li>apple</li>
</ol>
</div>
<div>
<span>Union set</span>
<ol id="union"></ol>
</div>
</div>
body {
font-family: monospace;
}
.match {
color: cornflowerblue;
font-weight: bold;
text-decoration: underline;
}
#container {
display: flex;
gap: 1rem;
justify-content: space-evenly;
}
const listOneItems = document.querySelectorAll("#set-one li");
const listTwoItems = document.querySelectorAll("#set-two li");
const allListItems = document.querySelectorAll("li");
const unionList = document.getElementById("union");
// Create sets for each list
const set1 = new Set();
const set2 = new Set();
// Add list items to each set
listOneItems.forEach((item) => {
set1.add(item.textContent);
});
listTwoItems.forEach((item) => {
set2.add(item.textContent);
});
// Create a union of both sets
const unionSet = set1.union(set2);
// List the items in the union
unionSet.forEach((item) => {
const li = document.createElement("li");
li.textContent = item;
unionList.appendChild(li);
});
我们使用每个列表项的 HTML textContent,因此我们得到的是字符串 Set,但您可以看到,如果我们考虑到 Set 可以包含数组或对象等数据类型,这会多么强大。
这很有帮助,因为我们不需要任何自定义实现来删除重复项、执行相等性检查或进行其他比较。一旦我们将元素放入 Set 中,我们就知道它们都是唯一的,而 union 方法是创建包含出现在“任一或两者” Set 中的(唯一)项目的第三个 Set 的优化方式。
通过 Set 交集,我们可以检查两个 Set 中共同存在的元素,以查看重叠部分。在此示例中,我们不将交集 Set 显示为第三个列表,就像我们之前对 union 所做的那样,而是可以使用它来突出显示“仅属于这两个 Set”的元素。
<div id="container">
<div>
<span>Set one</span>
<ol id="set-one">
<li>apple</li>
<li>banana</li>
<li>pear</li>
<li>carrot</li>
<li>plum</li>
</ol>
</div>
<div>
<span>Set two</span>
<ol id="set-two">
<li>banana</li>
<li>peach</li>
<li>plum</li>
<li>celery</li>
<li>apricots</li>
</ol>
</div>
</div>
body {
font-family: monospace;
}
.match {
color: cornflowerblue;
font-weight: bold;
text-decoration: underline;
}
#container {
display: flex;
gap: 1rem;
justify-content: space-evenly;
}
const listOneItems = document.querySelectorAll("#set-one li");
const listTwoItems = document.querySelectorAll("#set-two li");
const allListItems = document.querySelectorAll("li");
// Create sets for each list
const set1 = new Set();
const set2 = new Set();
// Add list items to each set
listOneItems.forEach((item) => {
set1.add(item.textContent);
});
listTwoItems.forEach((item) => {
set2.add(item.textContent);
});
// Make the intersection set
const intersectionSet = set1.intersection(set2);
// Loop through lists and highlight if they're in the intersection
allListItems.forEach((item) => {
if (intersectionSet.has(item.textContent)) {
item.className = "match";
}
});
在查看差集之前,让我们先看看 symmetricDifference,它听起来可能很复杂,但希望下面的示例能使其变得简单。对称差集允许我们检查哪些元素存在于任一 Set 中,但不包含两者。
<div id="container">
<div>
<span>Set one</span>
<ol id="set-one">
<li>apple</li>
<li>banana</li>
<li>pear</li>
<li>carrot</li>
<li>plum</li>
</ol>
</div>
<div>
<span>Set two</span>
<ol id="set-two">
<li>banana</li>
<li>peach</li>
<li>plum</li>
<li>celery</li>
<li>apricots</li>
</ol>
</div>
</div>
body {
font-family: monospace;
}
.match {
color: cornflowerblue;
font-weight: bold;
text-decoration: underline;
}
#container {
display: flex;
gap: 1rem;
justify-content: space-evenly;
}
const listOneItems = document.querySelectorAll("#set-one li");
const listTwoItems = document.querySelectorAll("#set-two li");
const allListItems = document.querySelectorAll("li");
// Create sets for each list
const set1 = new Set();
const set2 = new Set();
// Add list items to each set
listOneItems.forEach((item) => {
set1.add(item.textContent);
});
listTwoItems.forEach((item) => {
set2.add(item.textContent);
});
const setNotBoth = set1.symmetricDifference(set2);
allListItems.forEach((item) => {
if (setNotBoth.has(item.textContent)) {
item.className = "match";
}
});
如果这一切对您来说都是新的并且您正在努力理解,请将对称差集与交集示例进行比较。您应该会看到 symmetricDifference 方法执行的操作与 intersection 的逻辑操作相反。
通过 Set 差集,我们可以检查一个 Set 中存在但另一个 Set 中不存在的元素。对于这个例子,我们创建了两个新 Set 而不是一个。第一个(set1only)是通过 set1.difference(set2) 创建的,我们得到的是一个包含“仅属于 Set one”的新 Set。我们对新 Set set2only 做同样的事情,以查找属于 Set two 但不属于 Set one 的项目。对于每个列表,我们可以使用用 difference 创建的 Set 来突出显示未出现在另一个列表中的列表项。
<div id="container">
<div>
<span>Set one</span>
<ol id="set-one">
<li>apple</li>
<li>banana</li>
<li>pear</li>
<li>carrot</li>
<li>plum</li>
</ol>
</div>
<div>
<span>Set two</span>
<ol id="set-two">
<li>banana</li>
<li>peach</li>
<li>plum</li>
<li>celery</li>
<li>apricots</li>
</ol>
</div>
</div>
body {
font-family: monospace;
}
.setOneMatch {
color: cornflowerblue;
font-weight: bold;
text-decoration: underline;
}
.setTwoMatch {
color: orange;
font-weight: bold;
text-decoration: underline;
}
#container {
display: flex;
gap: 1rem;
justify-content: space-evenly;
}
const listOneItems = document.querySelectorAll("#set-one li");
const listTwoItems = document.querySelectorAll("#set-two li");
const allListItems = document.querySelectorAll("li");
// Create sets for each list
const set1 = new Set();
const set2 = new Set();
// Add list items to each set
listOneItems.forEach((item) => {
set1.add(item.textContent);
});
listTwoItems.forEach((item) => {
set2.add(item.textContent);
});
const set1only = set1.difference(set2);
const set2only = set2.difference(set1);
allListItems.forEach((item) => {
if (set1only.has(item.textContent)) {
item.className = "setOneMatch";
} else if (set2only.has(item.textContent)) {
item.className = "setTwoMatch";
}
});
最后要探索的新方法和概念是子集、超集和“不相交集”方法。现在,您已经熟悉了返回新 Set 的 Set 操作。子集、超集和不相交集方法不返回新 Set,而是返回一个 Boolean 值,以指示特定的状态或逻辑检查。
我们可以首先查看**子集**和**超集**。我们不突出显示列表项,而是有一些陈述或断言,例如“Set one 是 Set two 的子集”。然后,我们根据诸如 if (set1.isSupersetOf(set2)) 之类的检查结果,在列表项中添加一些文本(TRUE / FALSE)。
<div id="container">
<div>
<span>Set one</span>
<ul id="set-one">
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</div>
<div>
<span>Set two</span>
<ul id="set-two">
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
</ul>
</div>
</div>
<span>Subsets:</span>
<ul id="assertions">
<li id="1subset2">Set one is a subset of set two</li>
<li id="2subset1">Set two is a subset of set one</li>
</ul>
<span>Supersets:</span>
<ul id="assertions">
<li id="1superset2">Set one is a superset of set two</li>
<li id="2superset1">Set two is a superset of set one</li>
</ul>
const listOneItems = document.querySelectorAll("#set-one li");
const listTwoItems = document.querySelectorAll("#set-two li");
const assertItems = document.querySelectorAll("#assertions li");
// subsets
const oneSubTwo = document.getElementById("1subset2");
const twoSubOne = document.getElementById("2subset1");
// supersets
const oneSupTwo = document.getElementById("1superset2");
const twoSupOne = document.getElementById("2superset1");
// Create sets
const set1 = new Set();
const set2 = new Set();
// Add list items to each set
listOneItems.forEach((item) => {
set1.add(item.textContent);
});
listTwoItems.forEach((item) => {
set2.add(item.textContent);
});
if (set2.isSubsetOf(set1)) {
twoSubOne.textContent += " [TRUE]";
} else {
twoSubOne.textContent += " [FALSE]";
}
if (set1.isSupersetOf(set2)) {
oneSupTwo.textContent += " [TRUE]";
} else {
oneSupTwo.textContent += " [FALSE]";
}
if (set2.isSupersetOf(set1)) {
twoSupOne.textContent += " [TRUE]";
} else {
twoSupOne.textContent += " [FALSE]";
}
if (set1.isSubsetOf(set2)) {
oneSubTwo.textContent += " [TRUE]";
} else {
oneSubTwo.textContent += " [FALSE]";
}
body {
font-family: sans-serif;
}
#container {
display: flex;
gap: 1rem;
justify-content: space-evenly;
}
li {
font-weight: bold;
}
#container ul {
border-radius: 10px;
display: flex;
gap: 2rem;
padding: 1rem;
justify-content: space-evenly;
list-style: none;
border: 1px solid black;
}
#set-one {
background-color: paleturquoise;
}
#set-two {
background-color: lightsteelblue;
}
因此,使用**子集**,我们可以检查一个 Set 中的所有项目是否出现在另一个 Set 中。使用**超集**,我们做相反的事情,以查看一个 Set 是否包含另一个 Set 的所有项目,以及一些额外的项目。
我们要看的最后一个方法是 isDisjointFrom(),我们可以用它来找出两个 Set 是否**没有共同元素**。下面的第一个和第二个 Set 不互斥,因为它们有一个共同的元素(“C”)。第三个 Set 与其他两个 Set 不相交,因为它与任一 Set 都没有共同的项目。
<div id="container">
<div>
<span>Set one</span>
<ul id="set-one">
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</div>
<div>
<span>Set two</span>
<ul id="set-two">
<li>C</li>
<li>D</li>
<li>E</li>
</ul>
</div>
<div>
<span>Set three</span>
<ul id="set-three">
<li>F</li>
<li>G</li>
<li>H</li>
</ul>
</div>
</div>
<span>Disjoint from:</span>
<ul>
<li id="1disjointFrom2">Set one is disjoint from set two</li>
<li id="2disjointFrom3">Set two is disjoint from set three</li>
<li id="3disjointFrom1">Set three is disjoint from set one</li>
</ul>
body {
font-family: sans-serif;
}
#container {
display: flex;
gap: 1rem;
justify-content: space-evenly;
}
li {
font-weight: bold;
}
#container ul {
border-radius: 10px;
display: flex;
gap: 2rem;
padding: 1rem;
justify-content: space-evenly;
list-style: none;
border: 1px solid black;
}
#set-one {
background-color: paleturquoise;
}
#set-two {
background-color: lightsteelblue;
}
#set-three {
background-color: lavender;
}
const listOneItems = document.querySelectorAll("#set-one li");
const listTwoItems = document.querySelectorAll("#set-two li");
const listThreeItems = document.querySelectorAll("#set-three li");
// Create sets
const set1 = new Set();
const set2 = new Set();
const set3 = new Set();
// Add list items to each set
listOneItems.forEach((item) => {
set1.add(item.textContent.trim());
});
listTwoItems.forEach((item) => {
set2.add(item.textContent.trim());
});
listThreeItems.forEach((item) => {
set3.add(item.textContent.trim());
});
// Assertions
const oneDisjointTwo = document.getElementById("1disjointFrom2");
const twoDisjointThree = document.getElementById("2disjointFrom3");
const threeDisjointOne = document.getElementById("3disjointFrom1");
if (set1.isDisjointFrom(set2)) {
oneDisjointTwo.textContent += " [TRUE]";
} else {
oneDisjointTwo.textContent += " [FALSE]";
}
if (set2.isDisjointFrom(set3)) {
twoDisjointThree.textContent += " [TRUE]";
} else {
twoDisjointThree.textContent += " [FALSE]";
}
if (set3.isDisjointFrom(set1)) {
threeDisjointOne.textContent += " [TRUE]";
} else {
threeDisjointOne.textContent += " [FALSE]";
}
希望您喜欢这篇关于 Set 方法的博文,以及为什么我认为 Set 是一个有趣的概念。您知道在实际示例中有哪些不同的方法可以使用这些方法吗?请随时与我们联系,告诉我您的想法或我是否遗漏了什么!您应该已经为下一个项目做好了 Set 的准备,下次再见!