Когда использовать 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 шт):
Объекты 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
В обычных сетах мы можем хранить данные любого типа, а в слабых только объекты;
В слабых сетах нам доступны только методы add(), has(), delete(), и clear();
Объекты на которые нет ни одной внешней ссылки автоматически удаляются сборщиком мусора:
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 // из за особенностей реализации мы точно не знаем // когда будет удален конкретный объект, поэтому:Нельзя посмотреть на то что храниться в WeakSet'e:
console.log(weakSet) // WeakSet { <items unknown> }Неизвестен его размер (соответственно, отсутствует свойство size);
И как следствие вышеперечисленного, они не итерируемы.
Что и когда использовать:
Я не претендую на истину, если Вы не согласны с тем что написано ниже то сформулируйте собственное видение в комментарии.
Set vs Array
Используйте сеты вместо массивов если Вам нужно отсутствие дубликатов и/или важна скорость (Set'ы быстрее, что особенно заметно на больших коллекциях).
Set vs WeakSet
Используйте слабые сеты если Вы работаете только с объектами и Вам нужно повысить эффективность работы с данными. В примере со странами выше видно что удаление объекта из массива автоматически удаляет его и из WeakSet'a.
Добавлю своих размышлений.
Зачем нужен WeakSet? Для этого можно сначала подумать, зачем нужен WeakMap.
Предположим, есть некий объект -- продукт. И есть некий визуализатор, который формирует для этого продукта картинку. Процесс формирования тяжел, и визуализатор хочет картинку закэшировать. Он мог бы положить картинку в продукт, но:
- в идеале картинка приватна для визуализатора, только он будет ей пользоваться
- продукт может быть неизменяемым
- если визуализатор больше не нужен, то картинка будет болтаться в памяти вместе с продуктом
Визуализатор мог бы сохратить картинку у себя, но тогда получается что:
- если продукт больше не нужен, то картинка будет болтаться в памяти вместе с визуализатором.
В этот момент на помощь визуализатору приходит WeakMap, как способ работать с пересечением времен жизни объектов. Картинка будет храниться в WeakMap в визуализаторе только пока живы оба (визуализатор и продукт).
А WeakSet это просто пара к WeakMap, ну раз есть Map и Set, то логично иметь его для симметрии.
Можно хранить простые отношения объектов (лайки какие-то).
По поводу того, что Set быстрее массива, скорее всего это -- случайность.
В следующей версии браузера может стать наоборот, и опираться на это я бы не стал.
если на объект, хранимый в WeakSet нет ни одной внешней ссылки, то сборщик мусора удалит этот объект.
Мы не можем проверить, удалил ли он уже (сразу) объект, т. к. проверяем мы только has-ом, а для этого нужна ссылка на объект.
А если есть ссылка, то объект сборщик не удалит.
Так что, можно сказать, он удалит объект когда-то в конечном итоге.