Что такое WeakSet в JavaScript и как он работает
Перевод: Alex Devero — What WeakSet in JavaScript is and How It Works
WeakSet — один из новейших объектов в JavaScript, представляющий специфический тип коллекций. Многие разработчики JavaScript мало что знают о нем или вообще ничего не знают. В этой статье вы узнаете, что такое WeakSet в JavaScript, как он работает, а также когда он может быть полезен.
Краткое введение
WeakSet очень похож на Set (наборы). Если вы не знакомы с наборами, не волнуйтесь. Вам не обязательно иметь предварительные знания о наборах. Наборы можно использовать для хранения значений. Рассмотрим наборы на примере массивов.
Массивы, как и WeakSets и Sets, также являются коллекциями. Они также позволяют хранить различные значения, от чисел и строк до логических значений и объектов, даже наборов. На этом сходство заканчивается и начинают проявляться различия. Одно отличие состоит в том, что, в отличие от массивов, Sets могут содержать только уникальные значения.
С WeakSet эта разница идет еще дальше. WeakSet могут содержать только объекты. Если вы попытаетесь добавить что-нибудь еще, кроме объекта, JavaScript выдаст ошибку. Эти объекты также должны быть уникальными. То есть нельзя добавлять один и тот же объект дважды.
Еще одна важная особенность WeakSet — из за который они так называются («Weak — что означает слабый»), это то что все объекты, которые вы храните внутри WeakSet, «слабо» хранятся . То есть, если вы удалите все ссылки на объект, хранящийся в WeakSet, этот объект удалится сборщиком мусора.
Этот объект будет удален из памяти. Однако это не означает, что объект будет немедленно удален. Вначале он будет «помечен» для сборки мусора. И только когда запуститься процесс очистки от мусора, он будет удален. Есть еще одно важное различие между наборами и слабыми наборами, а также массивами. WeakSets не итерабельная коллекция (то есть не перебираемая).
Вы можете добавлять элементы или удалять любые существующие. Вы также можете проверить, содержит ли WeakSet конкретный элемент. Однако вы не можете перебрать его с помощью какого-либо цикла. Также нет свойства возвращающий размер коллекции.
Теперь давайте посмотрим, как вы можете создавать новые WeakSet.
Создание нового WeakSet
Если вы хотите создать новый WeakSet, вы должны использовать конструктор WeakSet(). Это функция создаст новый WeakSet, который затем можно будет использовать для хранения значений. Есть два способа использования конструктора WeakSet(). Вы можете использовать его для создания пустого WeakSet и добавления к нему значений позже. А так же вы можете передать итерацию со значениями в качестве параметра конструктору в тот момент, когда вы используете конструктор для создания нового WeakSet. Когда вы слышите слово «итерацию», то представьте себе некоторый набор значений. В этом случае итерируемый объект представляет собой массив. Итак, передадим массив с объектами.
// Создание нового пустого WeakSet способ 1 const myWeakSet = new WeakSet() // Создание нового WeakSet со значениями способ 2 const myWeakSet = new WeakSet([myObj1, myObj1])
Методы WeakSet
Мы уже немного поговорили о том, что позволяют делать WeakSet. Вы можете добавлять элементы в WeakSet и удалять их. Вы также можете проверить, содержит ли какой-либо WeakSet определенный элемент. Для каждой из этих задач есть свои методы. Давайте посмотрим на них.
Добавление новых объектов в WeakSet
Если вы хотите добавить объекты в WeakSet, вы можете сделать две вещи. Во-первых, вы можете передать эти объекты в конструктор WeakSet() при создании нового WeakSet. Во-вторых, вы можете добавлять объекты позже с помощью метода add (). Этот метод принимает один параметр — объект, который вы хотите сохранить.
Об этом следует помнить. Фактически он принимает только один объект. Если вы попытаетесь передать несколько объектов, только первый будет добавлен в WeakSet. Остальные будут проигнорированы. Итак, если вы хотите добавить несколько объектов, используйте несколько раз метод add().
// Добавление объекта через конструктор // Создаем несколько объектов let myObj1 = { name: 'Toby' } let myObj2 = { name: 'Christine' } // Создаем новый WeakSet const myWeakSet = new WeakSet([myObj1, myObj2]) // Создание объектов через метод add() // Создаем объекты let myObj1 = { name: 'Rafael' } let myObj2 = { name: 'Victoria' } // Создаем новый WeakSet const myWeakSet = new WeakSet() // Добавляем объекты myWeakSet.add(myObj1) myWeakSet.add(myObj2) // Так делать нельзя: let myObj1 = { name: 'Jack' } let myObj2 = { name: 'Julie' } const myWeakSet = new WeakSet() // Объект "myObj2" не будет добавлен в набор myWeakSet.add(myObj1, myObj2)
Удаление объектов из WeakSet
Удаление объектов из WeakSet просто и понятно. Если вы хотите удалить какой-либо объект, вы можете использовать метод delete(). Этот метод принимает один параметр, имя объекта, который вы хотите удалить. Как и add(), он работает с одним объектом.
Итак, если вы хотите удалить несколько объектов, вам нужно использовать несколько раз метод delete(). Когда вы используете этот метод, он всегда возвращает логическое значение. Он вернет истину, если объект был успешно удален. Если объект не хранится в WeakSet, он вернет false.
// Создаем несколько объектов let myObj1 = { language: 'JavaScript' } let myObj2 = { language: 'TypeScript' } let myObj3 = { language: 'Python' } // Создаем новый WeakSet // и добавляем первые два объекта const myWeakSet = new WeakSet([myObj1, myObj2]) // Удаляем объект "myObj1" myWeakSet.delete(myObj1) // true // Удаляем объект "myObj2" myWeakSet.delete(myObj2) // true // Пытаемся удалить объект "myObj3" myWeakSet.delete(myObj3) // false // Это не сработает: let myObj1 = { language: 'JavaScript' } let myObj2 = { language: 'TypeScript' } const myWeakSet = new WeakSet([myObj1, myObj2]) myWeakSet.delete(myObj1, myObj2) // true // Объект "myObj2" будет проигнорирован
Проверка наличия объекта в WeakSet
В WeakSet нельзя хранить одинаковые элементы и у них нет свойства размера. Это может затруднить определение того, существует ли конкретный объект в WeakSet или нет. К счастью, есть способ, которым вы можете это выяснить. Это метод has(). Подобно delete() и add() он также принимает один параметр.
Этот параметр — имя объекта, который вы хотите проверить. Когда вы используете этот метод, он также возвращает логическое значение, как и delete(). Он возвращает либо true, если объект существует в WeakSet, либо false, если он не существует.
let myObj1 = { language: 'React' } let myObj2 = { language: 'Vue.js' } let myObj3 = { language: 'Angular' } const myWeakSet = new WeakSet([myObj1, myObj2]) myWeakSet.has(myObj1) // Результат: // true myWeakSet.has(myObj2) // Результат: // true myWeakSet.has(myObj3) // Результат: // false
Нет свойств итераций и размера
Как вы знаете, одно различие между WeakSet и Set заключается в том, что WeakSet не итерируем. Другое отличие состоит в том, что WeakSet не имеют свойства размера. Это может может показаться странным. Но если задуматься, это действительно имеет смысл. Как мы уже говорили, все объекты внутри WeakSet слабо удерживаются.
Если какой-либо из этих объектов теряет все ссылки, он будет «помечен» для удаления из памяти. Когда происходит сборка мусора, этот объект будет удален из памяти. Особенность сборки мусора в том, что он работает, когда захочет. Вы не можете предсказать, когда это произойдет.
Допустим, у вас есть объект. Вы добавляете этот объект в WeakSet. Что произойдет, если вы удалите этот объект в другой части кода? Ответ в зависимости от обстоятельств. Это зависит от того, успела ли сборка мусора запуститься или нет. Если это так, объект удаляется из памяти, а также удаляется из WeakSet.
Давайте представим на мгновение, что вы можете использовать размер или перебирать WeakSet. Если вы переберете его перед сборкой мусора, вы получите один результат. Если вы выполните итерацию после сборки мусора, вы получите другую. То же и с размером. Вы получите два разных числа.
Вот почему нельзя перебирать WeakSet и нельзя полагаться на размер коллекции. Эти два параметра не будут надежными. Сейчас они скажут вам одно, а через секунду — совсем другое. Это было бы похоже на бросание кости.
А как насчет has()
Надеюсь, вы поняли, почему итерируемые WeakSet и свойство size не имеют смысла. А как насчет метода has()? С has() — совсем другая история. Подумайте, как работает этот метод или как вы его используете. Когда вы его используете, вы передаете имя объекта, который хотите проверить.
Это имя, имя переменной, является ссылкой. Когда вы передаете его, вы не передаете сам объект. Вместо этого вы передаете эту ссылку. Ссылка — это адрес памяти переменной. Это указатель на ячейку памяти, где хранится переменная.
Вернемся к сборке мусора. Сборка мусора собирает объекты только тогда, когда все ссылки на эти объекты удалены. В противном случае он оставляет их в покое. Когда вы используете метод has() и передаете ссылку на какой-либо объект, это означает, что все еще существует хотя бы одна ссылка на этот объект.
Это означает, что этот объект не был удален сборщиком мусора. Он все еще существует. Итак, если вы используете метод has(), вы получите достоверную информацию. Вот почему метод has() имеет смысл. Для has() требуется ссылка на существующий объект. Способность к итерации и свойство размера этого не требует.
Пример использования WeakSet
Из-за того, как он работают, WeakSet не очень часто используются. Если вы хотите сохранить какие-то значения, объекты или нет, лучшим выбором будет массив или Map. Один из сценариев, в котором могут быть полезны WeakSet, — это отслеживание существующих объектов. Вы можете хранить ссылки на эти объекты в массиве или в Map.
Это предотвратит удаления любого из этих объектов сборщиком мусора, если все другие ссылки на них исчезнут. Эти объекты останутся в памяти и потенциально могут вызвать утечку памяти. Используйте WeakSet для хранения этих объектов, и у вас больше не будет этой проблемы.
Одним из простых примеров может быть система входа в систему. Вы можете отслеживать пользователей (объекты), которые находятся в сети, добавляя их в WeakSet. Когда любой из этих пользователей покидает ваше приложение, вы удаляете соответствующий объект. Позже вы можете использовать метод has(), чтобы проверить, находится ли конкретный пользователь в сети, существует ли соответствующий объект или нет.
// Создаем трех пользователей, которые вошли в систему let user1 = { username: 'joey' } let user2 = { username: 'jack15' } let user3 = { username: 'skylar' } // Создаем новый WeakSet const loggedUsers = new WeakSet() // Добавляем "user1" в "loggedUsers" loggedUsers.add(user1) // Добавляем "user2" в "loggedUsers" loggedUsers.add(user2) // Добавляем "user3" в "loggedUsers" loggedUsers.add(user3) // Проверяем, присутствуют ли все пользователи // loggedUsers.has(user1) // Результат: // true // loggedUsers.has(user2) // Результат: // true // loggedUsers.has(user3) // Результат: // true // Позволяем "user2" и"user3" выйти из приложения user2 = null user3 = null // Проверяем, все ли пользователи вошли в систему loggedUsers.has(user1) // Результат: // true loggedUsers.has(user2) // Результат: // false loggedUsers.has(user3) // Результат: // false
Заключение: что такое WeakSet в JavaScript и как он работает
WeakSet — одна из функций, которую вы будете использовать нечасто. Однако это не значит, что оно совершенно бесполезно. Это не так. Есть некоторые вещи, которые WeakSet может делать лучше других. Я надеюсь, что это руководство помогло вам понять, что такое WeakSet, как они работают и когда могут быть полезны.
Краткое введение. В предложении «И только когда запуститься процесс очистки от мусора, он будет удалене» ошибка. Должно быть «И только когда запуститься процесс очистки от мусора, он будет удален»
Спасибо, за замечание.