Привет 2018! Прошло 3 года с тех пор, как мы впервые увидели Javascript 2015 (он же ES6). За это время большинство из нас сосредоточилось на косметических изменениях, таких как функции стрелок => или причудливый оператор разрушения . (Не убивайте за то, что называл их косметическими 💄 )

Каждому нужно что-то захватывающее, вроде предстоящего модного оператора |> pipe. Какая разница, добавил ли ES6 такие вещи, как WeakMap, WeakSet, Iterables, Map или Set. Даже смотреть на эту штуку под названием WeakMap так удручающе 😞

Оставив в стороне сарказм, давайте поговорим о WeakMaps: dancer

Зачем вам нужно что-то слабое

Я должен согласиться, что имя WeakMap определенно неправильное. Если бы это был я, я бы назвал это SuperMap. Прежде чем мы перейдем к определениям, давайте займемся моментом и поймем, зачем нам нужны WeakMap в наших приложениях.

Представьте, что это 1990 год, и вы создаете приложение всех стран 🎏, присутствующих в то время.

Пользователь может щелкнуть любую страну и получить подробную информацию, которая также включает площадь страны. Ниже представлена ​​функция вычисления гипотетической площади.

Кеширование области

Каждый раз, когда пользователь нажимает на страну, вы вычисляете площадь. Но у нас проблема! Если пользователь нажимает страну несколько раз, вам придется повторить это огромное асинхронное вычисление, чего мы должны полностью избегать. Обычно есть два способа решить эту проблему.

  1. Откажитесь от функции
  2. Кешировать функцию

Debouncing - это мирный способ успокоить множественные агрессивные заклинания за короткий промежуток времени. (Представьте себе нетерпеливого пользователя, который несколько раз нажимает кнопку обновления). Debounce позволяет нам принять только последний вызов и отбросить остальные.

Поскольку страны не так часто меняют регион, мы можем просто кэшировать результат calcArea.

Мы можем использовать как кэширование, так и устранение ошибок, чтобы наше приложение работало. Ниже приведена общая функция кэширования, которую мы будем использовать для кеширования calcArea.

Большой! Мы серьезно улучшили производительность.

Но у нас есть другая проблема, USSR только что ворвались в 15 новых стран. Это будет означать, что мы удалим СССР и добавим вновь образованные страны в наш массив countries.

Удаление USSR только из массива не помогает, так как наш кеш по-прежнему содержит USSR и вычисленную область. Наивным решением было бы обезьяно пропатчить нашу cachify функцию, чтобы удалить СССР, но если мир продолжит разбиваться на более мелкие страны, мы получим утечку памяти.

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

  1. Поддерживайте предварительно вычисленный массив площадей и синхронизируйте его со странами.
  2. Придумайте интеллектуальное вытеснение кеша, например LRU, по времени и т. Д.

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

Мы можем использовать интеллектуальную стратегию кэширования, такую ​​как Кэширование с наименьшим недавним использованием, при этом кэширование автоматически удаляет запись, которая использовалась менее всего в последнее время. Но у нас не хватает памяти с более чем 160 странами, и LRU не кажется таким уж волшебным и цельным.

А что насчет WeakMap?

WeakMap - это недостающий фрагмент мозаики для решения нашей проблемы с кешированием. Он автоматически удаляет из него * все неиспользуемые ссылки.

Объект WeakMap представляет собой набор пар ключ / значение, в которых ключи слабо связаны. Ключи должны быть объектами, а значения могут быть произвольными. - MDN

Мне нравится говорить, что WeakMap - это не что иное, как обычная карта с слабоумием. Это очень щадящая структура данных, она забудет вещи, которые больше не имеют значения. (Мы тоже должны быть такими: P)

Мы можем просто заменить Map на WeakMap в нашей функции кэширования.

Теперь позвольте USSR прорваться в 15 стран. Нам просто нужно позаботиться об удалении всех ссылок, указывающих на USSR obj в нашем приложении, и наша функция cachedCalcArea автоматически забудет USSR запись в кеше. Следовательно, избегая утечки памяти!

Как он все забывает?

WeakMap работает аналогично обычному Map, но для того, чтобы быть забывчивой версией Map, он накладывает следующие ограничения:

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

Давайте посмотрим на гипотетический пример WeakMap

Представьте, что экземпляр WeakMap - это здание с тысячами дверей.

  • var building = new WeakMap();

У каждой двери есть уникальный ключ, и у нас есть ключ: key: для нашего 🚪101. Из-за ограничений, упомянутых выше, ключ может быть только объектом.

  • var key = { password: '🔑' };

Мы можем запереть / отпереть нашу дверь этим ключом.

  • building.set(key, '🚪101'); building.get(key); // 🚪101

Теперь вор увидел наш ключ (его Javascript, черт возьми!) и пытается сфабриковать дубликат ключа.

  • var fake_key = { password: '🔑' };

Поскольку мы живем в мире Javascript, мы четко знаем, что даже если они выглядят одинаково, это не equal.

  • fake_key === key // false

Наш вор не читал эту замечательную статью и пытается проникнуть в наше здание, используя свой фальшивый ключ, но безуспешно :(.

  • building.get(fake_key); // undefined

Что будет, если мы потеряем ключ

Пока какая-то переменная содержит ссылку на наш исходный ключ, мы в безопасности. Но если наступит момент, когда ни одна переменная во всем приложении не содержит ссылку на наш ключ, мы потеряем доступ к нашему 🚪101.

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

Примечание. Это ключевое различие между WeakMap и Map. WeakMap удаляет <key,value>, если вы потеряете ключ, но на карте вы можете просто перечислить все ключи, чтобы найти потерянный ключ.

Возвращаясь к проблеме СССР, когда СССР распадается на 15 стран, и нам просто нужно позаботиться об удалении всех ссылок на объект СССР в нашем приложении.

Как вы можете видеть после вышеперечисленных шагов, в текущем состоянии приложения нет возможности получить доступ к объекту СССР, и с этим знанием сборщик мусора Javascript автоматически очищает память, зарезервированную для области СССР. Обратите внимание, что удаление происходит за кулисами, и все, что мы сделали, это заменили Map на WeakMap. Разве это не мощно?

Выводы из WeakMap

  • Помните, что не следует изменять ключевой объект, потому что в Javascript ссылка на объект остается неизменной, даже если вы изменяете объект.
  • WeakMap не может принимать примитивные значения javascript в качестве ключей. Вы должны использовать Map, если хотите использовать их в качестве ключа.
  • Иногда бывает быстрее не кэшировать функцию. Если для выполнения вашей функции требуется всего лишь миллисекунда, вы в конечном итоге замедляете ее путем кеширования.
  • В качестве значения WeakMap или Map можно использовать любой тип. Да еще обещает!
  • Удаление неподтвержденного ключа происходит не сразу. Это зависит от настроения сборщика мусора. Однако не стоит беспокоиться об этой части.
  • WeakMap отлично подходит для производного состояния. Часто ваше приложение имеет состояние, которое может быть просто получено из другого состояния. В приведенном ниже примере вы можете увидеть, что получение значения с использованием кэшированной функции намного проще в обслуживании и с ним легче работать.

Я очень надеюсь, что эта статья помогла вам понять WeakMaps. Мне нравится использовать его с такими библиотеками, как Immutable.js или Redux, поскольку они обеспечивают неизменность. Даже если вы не используете эти библиотеки, пока вы не изменяете объект, вы можете воспользоваться преимуществами WeakMap.

Я планирую написать Part-2 андердогов Javascript, дайте мне знать в комментариях, какую функцию Javascript вы считаете удивительной, но недооцененной.

Если вы эту статью, пожалуйста, поделитесь этой статьей, чтобы рассказать о ней.

Напишите мне в Twitter @ kushan2020.

Первоначально опубликовано на gist.github.com.