Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 55 additions & 55 deletions 1-js/05-data-types/08-weakmap-weakset/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,80 +110,80 @@ john = null; // перезаписываем ссылку на объект
## Пример: дополнительные данные


The main area of application for `WeakMap` is an *additional data storage*.
В основном `WeakMap` используется в качестве *дополнительного хранилища данных*.

There are objects managed elsewhere in the code, maybe they come from a third-party code, and in our code we need to keep additional information that is only relevant while the object is in memory.
Если мы работаем с объектом, который "принадлежит" другому коду, может быть даже сторонней библиотеке, и хотим сохранить у себя какие-то данные для него, которые должны существовать лишь пока существует этот объект, то `WeakMap` - отличный выбор.

And when the object is garbage collected, that data should automatically disappear as well.
Мы кладём эти данные в `WeakMap`, используя объект как ключ, и когда сборщик мусора удалит объекты из памяти, ассоциированные с ними данные тоже автоматически исчезнут.

```js
weakMap.set(john, "secret documents");
// if john dies, secret documents will be destroyed automatically
weakMap.set(john, "секретные документы");
// если john умрёт, "секретные документы" будут автоматически уничтожены
```

Let's look at an example.
Давайте рассмотрим один пример.

For instance, we have code that keeps a visit count for each user. The information is stored in a map: a user object is the key and the visit count is the value. When a user leaves (its object gets garbage collected), we don't want to store their visit count anymore.
Предположим, у нас есть код, которые ведёт учёт посещений для пользователей. Информация хранится в коллекции `Map`: объект, представляющий пользователя, является ключом, а количество визитов -- значением. Когда пользователь нас покидает (его объект удаляется сборщиком мусора), то больше нет смысла хранить соответствующий счётчик посещений.

Here's an example of a counting function with `Map`:
Вот пример реализации счётчика посещений с использованием `Map`:

```js
// 📁 visitsCount.js
let visitsCountMap = new Map(); // map: user => visits count
let visitsCountMap = new Map(); // map: пользователь => число визитов

// increase the visits count
// увеличиваем счётчик
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(count + 1);
}
```

Let's imagine another part of the code using it:
А вот другая часть кода, возможно, в другом файле, которая использует `countUser`:

```js
// 📁 main.js
let john = { name: "John" };

countUser(john); // count his visits
countUser(john); //ведём подсчёт посещений
countUser(john);

// later john leaves us
// пользователь покинул нас
john = null;
```

Now, we have a problem: `john` object should be garbage collected, but remains is memory, as it's a key in `visitsCountMap`.
И сейчас появилась проблема: объект `john` должен быть удалён сборщиком мусора, но он продолжает оставаться в памяти, как и его счётчик посещений в `visitsCountMap`.

We need to clean up `visitsCountMap` when we remove users, otherwise it will grow in memory indefinitely. Such cleaning can become a tedious task in complex architectures.
Нам нужно очищать `visitsCountMap` при удалении объекта пользователя, иначе коллекция будет бесконечно расти. Подобная очистка может быть неудобна в реализации при сложной архитектуре приложения.

We can avoid it by switching to `WeakMap` instead:
Проблемы можно избежать, если использовать `WeakMap`:

```js
// 📁 visitsCount.js
let visitsCountMap = new WeakMap(); // map: user => visits count
let visitsCountMap = new WeakMap(); // map: пользователь => число визитов

// increase the visits count
// увеличиваем счётчик
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(count + 1);
}
```

Now we don't have to clean `visitsCountMap`. After `john` is removed from memory, the additionally stored information from `WeakMap` will be removed as well.
Теперь нет необходимости вручную очищать `visitsCountMap`. После удаления объекта `john` из памяти вся ассоциированная с ним дополнительная информация будет также удалена из `WeakMap`.

## Use case: caching
## Применение для кеширования

Another common example is caching: when a function result should be remembered ("cached"), so that future calls on the same object reuse it.
Другая частая сфера применения -- это кеширование, когда результат вызова функции должен где-то запоминаться ("кешироваться") для того, чтобы дальнейшие её вызовы на том же объекте могли просто брать уже готовый результат, повторно используя его.

We can use `Map` for it, like this:
Для решения данной задачи мы можем использовать `Map`, вот так:

```js run
// 📁 cache.js
let cache = new Map();

// calculate and remember the result
// вычисляем и запоминаем результат
function process(obj) {
if (!cache.has(obj)) {
let result = /* calculate the result for */ obj;
let result = /* вычисляем результат для объекта */ obj;

cache.set(obj, result);
}
Expand All @@ -192,36 +192,36 @@ function process(obj) {
}

*!*
// Usage in another file:
// Используем в другом файле:
*/!*
// 📁 main.js
let obj = {/* some object */};
let obj = {/* какой-то объект */};

let result1 = process(obj); // calculated
let result1 = process(obj); // вычислен результат

// ...later, from another place of the code...
let result2 = process(obj); // taken from cache
// ...позже, из другого места в коде...
let result2 = process(obj); // берём из кеша

// ...later, when the object is not needed any more:
// ...позже, когда объект больше не нужен:
obj = null;

alert(cache.size); // 1 (Ouch! It's still in cache, taking memory!)
alert(cache.size); // 1 (Упс! Кеш до сих пор занимает память!)
```

Now for multiple calls of `process(obj)` with the same object, it only calculates the result the first time, and then just takes it from `cache`. The downside is that we need to clean `cache` when the object is not needed any more.
Сейчас многократные вызовы `process(obj)` с тем же самым объектом в качестве аргумента ведут к тому, что результат вычисляется только в первый раз, а затем последующие вызовы берут его из кеша. Недостатком является то, что необходимо вручную очищать `cache` от ставших ненужными объектов.

If we replace `Map` with `WeakMap`, then the cached result will be removed from memory automatically after the object gets garbage collected:
Но если мы будем использовать `WeakMap` вместо `Map`, то закешированные результаты будут автоматически удалены из памяти сборщиком мусора:

```js run
// 📁 cache.js
*!*
let cache = new WeakMap();
*/!*

// calculate and remember the result
// вычисляем и запоминаем результат
function process(obj) {
if (!cache.has(obj)) {
let result = /* calculate the result for */ obj;
let result = /* вычисляем результат для объекта */ obj;

cache.set(obj, result);
}
Expand All @@ -230,16 +230,16 @@ function process(obj) {
}

// 📁 main.js
let obj = {/* some object */};
let obj = {/* какой-то объект */};

let result1 = process(obj);
let result2 = process(obj);

// ...later, when the object is not needed any more:
// ...позже, когда объект больше не нужен:
obj = null;

// Can't get cache.size, as it's a WeakMap, but it's 0 or soon be 0
// When obj gets garbage collected, cached data will be removed as well
// Нет возможности получить cache.size, так как это `WeakMap`, но он равен 0 или скоро будет равен 0
// Когда сборщик мусора удаляет obj, связанные с ним данные из кеша тоже удаляются
```

## WeakSet
Expand All @@ -251,9 +251,9 @@ obj = null;
- Как и `Set`, он поддерживает `add`, `has` и `delete`, но не `size`, `keys()` и не является перебираемым.


Being "weak", it also serves as an additional storage. But not for an arbitrary data, but rather for "yes/no" facts. A membership in `WeakSet` may mean something about the object.
Будучи "слабой" версией оригинальной структуры данных, он тоже служит в качестве дополнительного хранилища. Но не для произвольных данных, а скорее для значений типа "да/нет". Присутствие во множестве `WeakSet` может что-то рассказать нам об объекте.

For instance, we can use `WeakSet` to keep track of users that visited our site:
Например, мы можем использовать `WeakSet` для учёта, кто из пользователей пользовался нашим сервисом:

```js run
let visitedSet = new WeakSet();
Expand All @@ -262,32 +262,32 @@ let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

visitedSet.add(john); // John visited us
visitedSet.add(pete); // Then Pete
visitedSet.add(john); // John again
visitedSet.add(john); // John заходил к нам
visitedSet.add(pete); // потом Pete
visitedSet.add(john); // John снова

// visitedSet has 2 users now
// visitedSet сейчас содержит двух пользователей

// check if John visited?
// проверим, заходил ли John?
alert(visitedSet.has(john)); // true

// check if Mary visited?
// проверим, заходила ли Mary?
alert(visitedSet.has(mary)); // false

// John object is not needed any more
// Объект John больше не нужен
john = null;

// visitedSet will be cleaned automatically
// структура данных visitedSet будет очищена автоматически
```

The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterations, and inability to get all current content. That may appear inconvenient, but does not prevent `WeakMap/WeakSet` from doing their main job -- be an "additional" storage of data for objects which are stored/managed at another place.
Наиболее значительным ограничением `WeakMap` и `WeakSet` является то, что их нельзя перебрать или взять всё содержимое. Это может доставлять неудобства, но не мешает `WeakMap/WeakSet` выполнять их главную задачу -- быть дополнительным хранилищем данных для объектов, управляемых из каких-то других мест в коде.

## Summary
## Итого

`WeakMap` is `Map`-like collection that allows only objects as keys and removes them once they become inaccessible by other means.
WeakMap` -- это `Map`-подобная коллекция, позволяющая использовать в качестве ключей только объекты, и автоматически удаляющая их вместе с соответствующими значениями, как только они становятся недостижимыми иными путями.

`WeakSet` is `Set`-like collection that only stores objects and removes them once they become inaccessible by other means.
`WeakSet` -- это `Set`-подобная коллекция, которая только хранит объекты и удаляет их, как только они становятся недостижимыми иными путями.

Both of them do not support methods and properties that refer to all keys or their count. Only individial get/has/set/remove operations with a given key are allowed.
Обе этих структуры данных не поддерживают методы и свойства, работающие со всем содержимым сразу или возвращающие информацию о размере коллекции. Возможны только операции на отдельном элементе коллекции.

`WeakMap` and `WeakSet` are used as "secondary" data structures in addition to the "main" object storage. Once the object is removed from the main storage, if it is only found as the key of `WeakMap` or in a `WeakSet`, it will be cleaned up automatically.
`WeakMap` и `WeakSet` используются как вспомогательные структуры данных в дополнение к "основному" месту хранения объекта. Если объект удаляется из основного хранилища и нигде не используется, кроме как в качестве ключа в `WeakMap` или в `WeakSet`, то он будет удалён автоматически.