O que são sets em JavaScript e como eles diferem dos WeakSets?

Em JavaScript, Set é um objeto embutido para gerenciar coleções de dados. Ele permite armazenar valores únicos de qualquer tipo, sejam primitivos ou referências a objetos. Set garante que cada valor nele apareça apenas uma vez, tornando-o útil para eliminar duplicatas de um array ou lidar com coleções de valores distintos. Quanto ao WeakSet, ele é um tipo especial de Set com menos recursos que permite armazenar referências a objetos e símbolos mantidos de forma fraca. Diferente do Set, WeakSet não suporta primitivos como números ou strings. Diferente de um Set comum, um WeakSet armazena apenas objetos, e as referências a esses objetos são "fracas", o que significa que WeakSets não impedem que os objetos armazenados sejam coletados pelo garbage collector se não houver outras referências a eles. Em termos mais simples, se o objeto não estiver sendo usado em nenhuma outra parte do seu código, ele é removido automaticamente para liberar memória. Para criar um Set, você usa o construtor Set e o atribui a uma variável:
const myFirstSet = new Set();
Você também pode inicializar o Set com valores:
const treeSet = new Set(["Baobab", "Jackalberry", "Mopane Tree", "Breadfruit"]);
Se você registrar o Set no console, a saída será assim:
/*
Set(4) {"Baobab", "Jackalberry", "Mopane Tree", "Breadfruit"}
  [[Entries]]
  0: "Baobab"
    value: "Baobab"
  1: "Jackalberry"
    value: "Jackalberry"
  2: "Mopane Tree"
    value: "Mopane Tree"
  3: "Breadfruit"
    value: "Breadfruit"
  size: 4
  [[Prototype]]: Set
*/
Se você não inicializou o Set com valores, pode usar o método add() para adicionar um item ao Set:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");
O resultado e a aparência no console permanecem os mesmos. Não esqueça que itens duplicados serão ignorados no Set:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");
treeSet.add("Baobab"); //duplicate item will be ignored

console.log(treeSet);
// Set(4) {"Baobab", "Jackalberry", "Mopane Tree", "Breadfruit"}
Os outros métodos que você pode usar para manipular um Set são:
  • delete()
  • clear()
  • has()
  • entries()
  • forEach()
  • keys()
  • values()
Vamos ver como esses métodos funcionam um por um. delete() remove um item especificado do Set:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");

treeSet.delete("Breadfruit");

console.log(treeSet); // Set(3) {"Baobab", "Jackalberry", "Mopane Tree"}
has() verifica se um valor especificado existe no Set:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");

treeSet.delete("Breadfruit");

console.log(treeSet.has("Breadfruit")); // false
entries() retorna um iterador Set contendo um array dos valores em formato [value, value]:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");

treeSet.delete("Breadfruit");

console.log(treeSet.entries());
// SetIterator {"Baobab" => "Baobab", "Jackalberry" => "Jackalberry", "Mopane Tree" => "Mopane Tree"}
keys() e values() mostram os valores no Set. keys() é apenas um alias para o método values():
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");

treeSet.delete("Breadfruit");

console.log("Keys: ", treeSet.keys());
console.log("Values: ", treeSet.values());
// Keys: SetIterator {"Baobab", "Jackalberry", "Mopane Tree"}
// Values: SetIterator {"Baobab", "Jackalberry", "Mopane Tree"}
forEach() permite iterar através do Set:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");

treeSet.delete("Breadfruit");

treeSet.forEach((tree) => console.log(tree));
/*
Baobab
Jackalberry
Mopane Tree
*/
clear() remove todos os itens do array:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");

treeSet.delete("Breadfruit");

treeSet.clear();

console.log(treeSet); // Set(0) {size: 0}
Também vale mencionar que existe uma propriedade size que retorna o número de itens no Set:
const treeSet = new Set();

// Add items to the treeSet
treeSet.add("Baobab");
treeSet.add("Jackalberry");
treeSet.add("Mopane Tree");
treeSet.add("Breadfruit");

treeSet.delete("Breadfruit");

console.log(treeSet.size); // 3
Assim como Set, também existe um construtor WeakSet que você pode usar para criar um WeakSet:
const treeWeakSet = new WeakSet();
WeakSet também possui os métodos add(), delete() e has():
const treeWeakSet = new WeakSet();

const baobab = { name: "Baobab" };
const jackalberry = { name: "Jackalberry" };
const mopaneTree = { name: "Mopane Tree" };
const breadfruit = { name: "Breadfruit" };

treeWeakSet.add(baobab);
treeWeakSet.add(jackalberry);
treeWeakSet.add(mopaneTree);
treeWeakSet.add(breadfruit);

treeWeakSet.delete(jackalberry);
console.log(treeWeakSet.has(jackalberry)); // false

console.log(treeWeakSet);
Na saída, o conteúdo do WeakSet aparece assim:
/*
WeakSet {{…}, {…}, {…}}
  [[Entries]]
    0: value: {name: "Mopane Tree"}
    1: value: {name: "Baobab"}
    2: value: {name: "Breadfruit"}
  [[Prototype]]: WeakSet
*/
O conteúdo é visível aqui porque as variáveis dos objetos ainda mantêm referências fortes, mantendo esses objetos na memória. Lembre-se que WeakSets não são iteráveis, então não há como iterar sobre suas entradas ou acessá-las programaticamente. O que você vê na saída é apenas uma ajuda para depuração. Não esqueça que apenas símbolos e objetos com chaves e valores bem definidos são suportados. Adicionar um primitivo, como números ou strings, resultará em um erro:
treeWeakSet.add("Alan Smith");

console.log(treeWeakSet); // Invalid value used in weak set
//    at WeakSet.add (<anonymous>)
A principal diferença entre um Set e um WeakSet é que um Set armazena qualquer valor, enquanto um WeakSet pode armazenar apenas objetos. Aqui estão algumas outras diferenças notáveis entre um Set e um WeakSet: | Recurso | Set | WeakSet | | --- | --- | --- | | Tipo de valores armazenados | Armazena qualquer tipo de dado | Armazena apenas objetos | | Referência | Referência forte | Referência fraca | | Iteração | Suporta iteração com forEach e loops | Não suporta iteração | | Métodos e propriedades | add(), delete(), has(), keys(), values(), size e mais | Apenas add(), delete() e has() | | Caso de uso | Coleção geral de valores únicos e remoção de duplicatas de arrays | Rastreamento eficiente de memória para referências a objetos | Você pode ver as diferenças nos tipos de valores que os dois tipos de sets podem armazenar, seu suporte para iterar sobre os objetos armazenados e seus casos de uso ideais. Reserve um momento para ler o conteúdo desta tabela.
Este módulo não possui perguntas. Marque como concluído.