WeakSet и WeakMap в JavaScript
WeakSet и WeakMap - это слабые коллекции. Они похожи на Set и Map, но работают только с объектами и не мешают сборщику мусора удалить объект из памяти.
Обычно эти коллекции нужны реже, чем Set и Map. Их используют, когда дополнительные данные должны жить только пока жив сам объект.
Что значит слабая коллекция
В JavaScript память очищается автоматически. Если объект больше нигде не используется, движок может удалить его из памяти.
Обычные Map и Set хранят сильную ссылку на объект. Пока объект лежит в такой коллекции, он считается используемым.
let users = new Set()
let user = { name: 'Alex' }
users.add(user)
user = null
В примере объект все еще остается в Set, поэтому он не пропал окончательно.
WeakSet и WeakMap хранят ссылку слабее. Если объект больше нигде не нужен, наличие в слабой коллекции не будет удерживать его в памяти.
WeakMap
WeakMap хранит данные в формате ключ - значение, как обычный Map. Главное отличие - ключами могут быть только объекты.
let visits = new WeakMap()
let user = { name: 'Alex' }
visits.set(user, 5)
visits.get(user) // 5
Значением может быть любое значение: число, строка, массив, объект или функция.
Методы WeakMap
У WeakMap есть только несколько основных методов.
set()
Добавляет значение по объекту-ключу.
let userData = new WeakMap()
let user = { name: 'Alex' }
userData.set(user, { role: 'admin' })
get()
Возвращает значение по ключу.
userData.get(user) // { role: 'admin' }
has()
Проверяет, есть ли ключ в коллекции.
userData.has(user) // true
delete()
Удаляет данные по ключу.
userData.delete(user) // true
Пример использования WeakMap
WeakMap удобно использовать, когда нужно хранить дополнительные данные для объекта, но не хочется записывать их прямо в сам объект.
let userStatus = new WeakMap()
let user = {
name: 'Alex',
}
userStatus.set(user, 'online')
userStatus.get(user) // 'online'
Если объект user перестанет использоваться в программе, данные из WeakMap тоже смогут быть очищены.
WeakSet
WeakSet похож на Set, но хранит только объекты. Его удобно использовать, когда нужно отметить объект, не добавляя в него новое свойство.
let checkedUsers = new WeakSet()
let user = { name: 'Alex' }
checkedUsers.add(user)
checkedUsers.has(user) // true
Методы WeakSet
У WeakSet есть три основных метода.
add()
Добавляет объект в коллекцию.
let processedUsers = new WeakSet()
let user = { name: 'Alex' }
processedUsers.add(user)
has()
Проверяет, есть ли объект в коллекции.
processedUsers.has(user) // true
delete()
Удаляет объект из коллекции.
processedUsers.delete(user) // true
Пример использования WeakSet
WeakSet можно использовать для отметки уже обработанных объектов.
let processedUsers = new WeakSet()
function processUser(user) {
if (processedUsers.has(user)) {
return
}
processedUsers.add(user)
console.log(`Обрабатываем ${user.name}`)
}
let user = { name: 'Alex' }
processUser(user) // Обрабатываем Alex
processUser(user) // повторной обработки не будет
Такой подход не меняет сам объект user и не добавляет в него технические свойства.
Ограничения WeakSet и WeakMap
Слабые коллекции нельзя перебрать. У них нет forEach(), keys(), values(), entries() и свойства size.
Это связано с тем, что элементы могут быть удалены сборщиком мусора в любой момент, когда объект перестанет использоваться.
let userData = new WeakMap()
userData.size // undefined
Также в WeakMap и WeakSet нельзя добавить примитивы.
let weakSet = new WeakSet()
weakSet.add('Alex') // TypeError
Когда использовать
Если нужно хранить список, считать количество элементов или перебирать коллекцию, лучше использовать обычные Set и Map.
WeakSet и WeakMap подходят для служебных данных, которые привязаны к объектам и не должны удерживать эти объекты в памяти.
Итого
WeakMap хранит дополнительные данные по объектам-ключам. WeakSet хранит только объекты и помогает отмечать их состояние. В отличие от обычных коллекций, они не перебираются и не показывают размер, зато не мешают удалению объектов из памяти.