diff --git a/1-js/10-es-modern/10-set-map/article.md b/1-js/10-es-modern/10-set-map/article.md index c9fbbe8a..f1973f2a 100644 --- a/1-js/10-es-modern/10-set-map/article.md +++ b/1-js/10-es-modern/10-set-map/article.md @@ -24,9 +24,13 @@ map // в обычном объекте это было бы одно и то же alert( map.get(1) ); // 'num1' alert( map.get('1') ); // 'str1' + +alert( map.size ); // 3 ``` -Как видно из примера выше, для сохранения и чтения значений используются методы `get` и `set`, причём `set` можно чейнить. +Как видно из примера выше, для сохранения и чтения значений используются методы `get` и `set`, причём `set` можно чейнить. И ключи и значения сохраняются "как есть", без преобразований типов. + +Свойство `map.size` хранит общее количество записей в `map`. **При создании `Map` можно сразу инициализовать списком значений.** @@ -62,11 +66,14 @@ alert( visitsCountMap.get(user) ); // 123 [smart header="Как map сравнивает ключи"] Для проверки значений на эквивалентность используется алгоритм [SameValueZero](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-samevaluezero). Он аналогичен строгому равенству `===`, отличие -- в том, что `NaN` считается равным `NaN`. + +Этот алгоритм жёстко фиксирован в стандарте, его нельзя изменять или задавать свою функцию для него. [/smart] -Для удаления записей используется метод: +Для удаления записей: Для проверки существования ключа: @@ -122,6 +129,10 @@ for(let entry of recipeMap) { // то же что и recipeMap.entries() } ``` +[smart header="Перебор идёт в том же порядке, что и вставка"] +Перебор осуществляется в порядке вставки. Объекты `Map` гарантируют это, в отличие от обычных объектов `Object`. +[/smart] + Кроме того, у `Map` есть стандартный методы `forEach`, аналогичный массиву: ```js @@ -140,17 +151,134 @@ recipeMap.forEach( (value, key, map) => { ``` - - - - - - -Есть и другие методы: +У `Map` есть и другие свой методы: + +## Set + +`Set` -- коллекция для хранения множества значений, причём каждое значение может встречаться лишь один раз. + +Например, к нам приходят посетители, и мы хотели бы сохранять всех, кто пришёл. Повторные визиты не должны приводить к дубликатам. + +`Set` для этого отлично подходит: + +```js +//+ run +'use strict'; + +let set = new Set(); + +let vasya = {name: "Вася"}; +let petya = {name: "Петя"}; +let dasha = {name: "Даша"}; + +// посещения +set.add(vasya); +set.add(petya); +set.add(dasha); +set.add(vasya); +set.add(petya); + +alert( set.size ); // 3 + +set.forEach( user => alert(user.name ) ); // Вася, Петя, Даша +``` + +В примере выше многократные добавления одного и того же объекта в `set` не создают лишних копий. + +Альтернатива `Set` -- это массивы с поиском дубликата при каждом добавлении, но это гораздо хуже по производительности. Или же объекты, где в качестве ключа выступает какой-нибудь уникальный идентификатор посетителя. Но это менее удобно, чем простой и наглядный `Set`. + +Основные методы: + + +Перебор `Set` осуществляется через `forEach` или `for..of` аналогично `Map`: + +```js +//+ run +'use strict'; + +let set = new Set(["апельсины", "яблоки", "бананы"]); + +// то же, что: for(let value of set) +set.forEach((value, valueAgain, set) => { + alert(value); // апельсины, затем яблоки, затем бананы +}); +``` + +Заметим, что в `Map` у функции в `.forEach` три аргумента: ключ, значение, объект `map`. + +В `Set` для совместимости с `Map` сделано похожим образом -- у `.forEach`-функции также три аргумента. Но первые два всегда совпадают и содержат очередное значение множества. + +## WeakMap и WeakSet + +`WeakSet` -- особый вид `Set` не препятствующий сборщику мусора. То же самое -- `WeakMap` для `Map`. + +То есть, если некий объект присутствует только в `WeakSet/WeakMap` -- он удаляется из памяти. + +Это нужно для тех ситуаций, когда сами объекты используются где-то в другом месте кода, а здесь мы хотим хранить для них "вспомогательные" данные, существующие лишь пока жив объект. + +Например, у нас есть элементы на странице или, к примеру, пользователи, и мы хотим хранить для них вспомогательную инфомацию, например обработчики событий или просто данные, но действительные лишь пока объект, к которому они относятся, существует. + +Если поместить такие данные в `WeakMap`, а объект сделать ключом, то они будут автоматически удалены из памяти, когда удалится элемент. + +Например: +```js +// текущие активные пользователи +let activeUsers = [ + {name: "Вася"}, + {name: "Петя"}, + {name: "Маша"} +]; + +// вспомогательная информация о них, +// которая напрямую не входит в объект юзера, +// и потому хранится отдельно +let weakMap = new WeakMap(); + +weakMap[activeUsers[0]] = 1; +weakMap[activeUsers[1]] = 2; +weakMap[activeUsers[2]] = 3; + +alert( weakMap[activeUsers[0]].name ); // Вася + +activeUsers.splice(0, 1); // Вася более не активный пользователь + +// weakMap теперь содержит только 2 элемента + +activeUsers.splice(0, 1); // Петя более не активный пользователь + +// weakMap теперь содержит только 1 элемент +``` + +TODO WRITE MORE + + + + + + + + + + + + + + + + + + + -ToDo