Когда использовать Set a когда WeakSet

Когда использовать Set a когда WeakSet, в чем разница, что лучше и для чего?

var arr = [1, 2, 3, 4, 5]; 

и

var set = new Set([1, 2, 3, 4, 5]); 

и

var weak_set = new WeakSet([1, 2, 3, 4, 5]);

В документации расписано но непонятно где это стоит применять и зачем.


Ответы (2 шт):

Автор решения: Vasily

Объекты Set это коллекции уникальных значений, которые могут хранить в себе данные любого типа.

Сделаем простой сет и посмотрим на него:

const numbers = [7, 1, 1, 2, 3, 5, 8] 

const set = new Set(numbers)

console.log(set)
// Set { 7, 1, 2, 3, 5, 8 }

как мы видим он содержит только уникальные значения из массива numbers, а их порядок остался неизменным.


Это делает объекты Set одним из самых простых способов для удаления дубликатов из массива:

const uniqueNumbers = [...new Set(numbers)]
console.log(uniqueNumbers)
// [ 7, 1, 2, 3, 5, 8 ]

Именно это и является его ключевой особенность, вне зависимости от того какие значения будут переданы они будут сохранены лишь в единственном экземпляре.


Проверим это, воспользовавшись методом add():

set.add(100)
console.log(set)
// Set { 7, 1, 2, 3, 5, 8, 100 }

set.add(7)
console.log(set)
// Set { 7, 1, 2, 3, 5, 8, 100 }
// наш сет не изменился так как 
// уже содержал в себе значение 7

И запомним размер данного сета с помощью свойства size:

const setSize = set.size
console.log(setSize)
// 7

полный список методов и свойств можно посмотреть здесь.

Для объектов Set определен протокол перебора, поэтому мы можем перебирать их так же как и карты:

for (let item of set) {
  console.log(item) 
}

for (let key of set.keys()) {
  console.log(key) 
}

for (let value of set.values()) {
  console.log(value) 
}

// все эти варианты вернут 
// одинаковый результат:
// 7
// 1
// 2
// 3
// 5
// 8
// 100

Если мы воспользуемся методом entries(), то получим:

for (let entry of set.entries()) {
  console.log(entry) 
  // [ 7, 7 ]
  // [ 1, 1 ]
  // [ 2, 2 ]
  // [ 3, 3 ]
  // [ 5, 5 ]
  // [ 8, 8 ]
  // [ 100, 100 ]
}

так сделано для того что бы у Set была обратная совместимость с Map.


Чем отличается WeakSet

  1. В обычных сетах мы можем хранить данные любого типа, а в слабых только объекты;

  2. В слабых сетах нам доступны только методы add(), has(), delete(), и clear();

  3. Объекты на которые нет ни одной внешней ссылки автоматически удаляются сборщиком мусора:

    const countries = [
      {
        country: "Russia"
      },
      {
        country: "Australia"
      }
    ]
    
    const weakSet = new WeakSet()
    
    weakSet.add(countries[0])
    
    let hasRussia = weakSet.has(countries[0])
    console.log(hasRussia)
    // true
    
    countries.splice(0, 1)
    
    hasRussia = weakSet.has(countries[0])
    console.log(hasRussia)
    // false
    
    // из за особенностей реализации мы точно не знаем 
    // когда будет удален конкретный объект, поэтому:
    
  4. Нельзя посмотреть на то что храниться в WeakSet'e:

    console.log(weakSet)
    // WeakSet { <items unknown> }
    
  5. Неизвестен его размер (соответственно, отсутствует свойство size);

  6. И как следствие вышеперечисленного, они не итерируемы.

Что и когда использовать:

Я не претендую на истину, если Вы не согласны с тем что написано ниже то сформулируйте собственное видение в комментарии.

Set vs Array

Используйте сеты вместо массивов если Вам нужно отсутствие дубликатов и/или важна скорость (Set'ы быстрее, что особенно заметно на больших коллекциях).

Set vs WeakSet

Используйте слабые сеты если Вы работаете только с объектами и Вам нужно повысить эффективность работы с данными. В примере со странами выше видно что удаление объекта из массива автоматически удаляет его и из WeakSet'a.

→ Ссылка
Автор решения: Sergei Kirjanov

Добавлю своих размышлений.

Зачем нужен WeakSet? Для этого можно сначала подумать, зачем нужен WeakMap.

Предположим, есть некий объект -- продукт. И есть некий визуализатор, который формирует для этого продукта картинку. Процесс формирования тяжел, и визуализатор хочет картинку закэшировать. Он мог бы положить картинку в продукт, но:

  • в идеале картинка приватна для визуализатора, только он будет ей пользоваться
  • продукт может быть неизменяемым
  • если визуализатор больше не нужен, то картинка будет болтаться в памяти вместе с продуктом

Визуализатор мог бы сохратить картинку у себя, но тогда получается что:

  • если продукт больше не нужен, то картинка будет болтаться в памяти вместе с визуализатором.

В этот момент на помощь визуализатору приходит WeakMap, как способ работать с пересечением времен жизни объектов. Картинка будет храниться в WeakMap в визуализаторе только пока живы оба (визуализатор и продукт).

А WeakSet это просто пара к WeakMap, ну раз есть Map и Set, то логично иметь его для симметрии. Можно хранить простые отношения объектов (лайки какие-то).

По поводу того, что Set быстрее массива, скорее всего это -- случайность. В следующей версии браузера может стать наоборот, и опираться на это я бы не стал.

если на объект, хранимый в WeakSet нет ни одной внешней ссылки, то сборщик мусора удалит этот объект.

Мы не можем проверить, удалил ли он уже (сразу) объект, т. к. проверяем мы только has-ом, а для этого нужна ссылка на объект. А если есть ссылка, то объект сборщик не удалит. Так что, можно сказать, он удалит объект когда-то в конечном итоге.

→ Ссылка