- Операторы
- Управляющие инструкции
- JS Объекты
- Array
- Boolean
- Date
- Error
- Function
- Global
- JSON
- Math
- Number
- Object
- RegExp
- String
- Symbol
- Итераторы и генераторы
- Map и WeakMap
- Set и WeakSet
- Локализация
- браузер BOM
- HTML DOM
- События
- HTML Объекты
- Промисы, async/await
- Сетевые запросы
- Бинарные данные и файлы
- Разное
Map и WeakMap
Map
Map – это коллекция ключ/значение ([key, value]) как и Object
. Но основное отличие в том, что Map позволяет использовать ключи любого типа.
Методы и свойства
let map = new Map( [iterable] )
Метод Map() создает новый Map-объект
Параметр
- iterable
- Необязательный. Массив или другой итерируемый объект, элементы которого являются пары ключ-значение. Например:
let myMap = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ])
map.size
Свойство size возвращает текущее количество элементов.
let map = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ]); alert ( map.size );
map.set( key, value )
Метод set() записывает по ключу
key
значениеvalue
и возвращает этот же объект.let map = new Map(); map.set('bar', 'foo'); map.set(1, 'foobar'); map.set('bar', 'baz'); // обновить элемент
Этот же пример можно записать так:
let map = new Map(); map.set('bar', 'foo') .set(1, 'foobar'); .set('bar', 'baz'); // обновить элемент
Map может использовать объекты в качестве ключей.
let john = { name: "John" }; // давайте сохраним количество посещений для каждого пользователя let visitsCountMap = new Map(); // объект john - это ключ для значения в объекте Map visitsCountMap.set(john, 123); alert(visitsCountMap.get(john)); // 123
map.get( key )
Метод get() возвращает значение по ключу или
undefined
, если ключkey
отсутствует.let map = new Map(); map.set('bar', 'foo'); alert( map.get('bar') + '\n' + map.get('bad') );
map.has( key )
Метод has() возвращает
true
, если ключkey
присутствует в коллекции, иначеfalse
.let map = new Map([ ['bar', "foo"] ]); alert( map.has('bar') + '\n' + map.has('bad') );
map.delete( key )
Метод delete() удаляет элемент по ключу
key
и возвращаетtrue
, если элемент в объектеMap
существовал и был удален, илиfalse
, если элемент не существует.let map = new Map(); map.set('bar', 'foo'); alert( map.delete('bar') + '\n' + map.delete('bar') );
map.clear()
Метод clear() очищает коллекцию от всех элементов и возвращает
undefined
.let map = new Map(); map.set( 'bar', 'baz' ).set( 1, 'foo' ); let s = map.size + '\n' + map.has( 'bar' ); map.clear(); alert( s+'\n\n' + map.size + '\n' + map.has( 'bar' ) );
map.keys()
Метод keys() возвращает итерируемый объект по ключам.
let map = new Map(); map.set('0', 'foo') .set(1, 'bar') .set({}, 'baz'); var Iter = map.keys(); alert (` ${ Iter.next().value } ${ Iter.next().value } ${ Iter.next().value } `);
map.values()
Метод values() возвращает итерируемый объект по по значениям.
let map = new Map(); map.set('0', 'foo') .set(1, 'bar') .set({}, 'baz'); var Iter = map.values(); alert (` ${ Iter.next().value } ${ Iter.next().value } ${ Iter.next().value } `);
map.entries()
Метод entries() возвращает итерируемый объект по парам вида
[ключ, значение]
, этот вариант используется по умолчанию вfor..of
.let map = new Map(); map.set('0', 'foo') .set(1, 'bar') .set({}, 'baz'); var Iter = map.entries(); let s = ` ${ Iter.next().value } ${ Iter.next().value } ${ Iter.next().value } \n`; for (let k of map) { // то же самое, что и map.entries() s += k+'\n'; } s+='\n'; for (let [key, value] of map) { s += `${key}: ${value}\n`; } alert (s);
map[Symbol.iterator]
Метод map[Symbol.iterator] возвращает новый объект Iterator, который содержит
массив[key, value]
для каждого элемента вMap-объекте
в порядке вставки.let map = new Map(); map.set('0', 'foo') .set(1, 'bar') .set({}, 'baz'); var Iter = map[Symbol.iterator](); // тоже, что map.entries() let s = ` ${ Iter.next().value } ${ Iter.next().value } ${ Iter.next().value } \n`; for (let k of map) { // то же самое, что и map.entries() s += k+'\n'; } alert (s);
map.forEach(callbackFn[, thisArg])
Метод forEach() Вызывается функцию
callbackFn
один раз для каждой пары ключ-значение (key,value), присутствующей в Map-объекте, в порядке вставки. Если указан параметрthisArg
, он будет использоваться в качествеthis
значения для каждого обратного вызова. Метод возвращаетundefined
Метод forEach() выполняет
callback
по одному разу для каждого значения, которое находится в объекте Set. Функция не будет выполняться для значений, которые были удалены. Тем не менее функция выполнится с элементами, значение которыхundefined
.callback
вызывается с тремя аргументами:1. значение элемента
2. ключ элемента
3. Set-объект обходаlet s = ''; function MapElements(value, key, map) { s += `map.get('${key}') = ${value}\n`; } map = new Map([['foo', 3], ['bar', {}], ['baz', undefined]]); map.forEach(MapElements); alert (s);
Использование NaN в качестве ключей Map
Чтобы сравнивать ключи, объект Map
использует алгоритм SameValueZero. Это почти такое же сравнение, что и ===
, с той лишь разницей, что NaN
считается равным NaN
. Так что NaN
также может использоваться в качестве ключа.
WeakMap
WeakMap представляет развитие коллекции Map. Первое отличие от Map в том, что ключи в WeakMap должны быть объектами, а не примитивными значениями.
Синтаксис
weakMap = new WeakMap([iterable])
Параметры
- iterable
- Необязательный. Может быть массивом или любым другим итерируемым объектом, элементы которого являются парами ключ-значение (массивы из двух элементов). Каждая пара ключ-значение будет добавлена во вновь созданный экземпляр WeakMap.
Null
обрабатывается какundefined
.
WeakMap не поддерживает перебор и методы keys()
, values()
, entries()
, так что нет способа взять все ключи или значения из неё.
В WeakMap
присутствуют только следующие методы:
weakMap.set(key, value)
. Метод записывает по ключуkey
значениеvalue
и возвращает этот же объект.weakMap.get(key)
. Возвращает значение по ключу илиundefined
, если ключkey
отсутствует.weakMap.delete(key)
. Возвращаетtrue
, если элемент в объекте WeakMap был успешно удален илиfalse
, если ключ не найден в WeakMap или если ключ не является объектом.weakMap.has(key)
. Возвращаетtrue
, если ключkey
присутствует в коллекции, иначеfalse
Пример использования объекта WeakMap
const wm1 = new WeakMap(), wm2 = new WeakMap(), wm3 = new WeakMap(); const o1 = {}, o2 = function(){}, o3 = window, o4 = [1, 2, 3]; wm1.set(o1, 37); wm1.set(o2, 'azerty'); wm2.set(o1, o2); // значением может быть что угодно, включая объект или функцию wm2.set(o3, undefined); wm2.set(wm1, wm2); // ключами и значениями могут быть объекты. Даже WeakMap-ами wm1.get(o2); // 'azerty' wm2.get(o2); // undefined, нет значения для o2 в wm2 wm2.get(o3); // undefined, это установленное значение wm1.has(o2); // true wm2.has(o2); // false wm2.has(o3); // true (даже если значение равно 'undefined') wm3.set(o1, 37); wm3.get(o1); // 37 wm1.has(o1); // true wm1.delete(o1); wm1.has(o1); // false
Вторым отличием от Map - cлабые (weaks) ссылки.
Объекты передаются в WeakMap по ссылке. И когда объект перестает существовать в силу различных причин, он удаляется из WeakMap. Рассмотрим следующий пример:
let jsCode = {code: "js"}, tsCode = {code: "ts"}; let js = {lang: "JavaScript"}, ts = {lang: "TypeScript"}; const weakMap = new WeakMap([[jsCode, js], [tsCode, ts]]); jsCode = null; console.log(weakMap); // WeakMap {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}} console.log("Некоторая работа"); const timerId = setTimeout(function(){ console.log(weakMap); // WeakMap {{code: "ts"} => {lang: "TypeScript"}} clearTimeout(timerId); }, 10000);
В данном случае сначала объект WeakMap хранит ссылки на два элемента с ключами jsCode
и tsCode
. Далее для переменной jsCode
устанавливается значение null
.
jsCode = null;
Это приведет к тому, что спустя некоторое время начальное значение этой переменной будет удалено сборщиком мусора JavaScript.
Причем если сразу после этого мы посмотрим на содержимое weakMap, то увидим, что объект с ключом jsCode
в нем еще присутствует. Однако спустя некоторое время ссылка будет удалена из weakMap. Для эмуляции прошествия времени здесь используется функция setTimeout
, которая выводит на консоль содержимое weakMap через 10 секунд (конкретный период времени, через который сборщик мусора удалит значение, может отличаться)
Теперь сравним с тем, что произойдет, если вместо WeakMap использовать Map:
let jsCode = {code: "js"}, tsCode = {code: "ts"}; let js = {lang: "JavaScript"}, ts = {lang: "TypeScript"}; const map = new Map([[jsCode, js], [tsCode, ts]]); jsCode = null; console.log(map); // Map(2) {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}} console.log("Некоторая работа"); const timerId = setTimeout(function(){ console.log(map); // Map(2) {{code: "js"} => {lang: "JavaScript"}, {code: "ts"} => {lang: "TypeScript"}} clearTimeout(timerId); }, 10000);
В случае с Map даже спустя некоторое время мы увидим, что в объекте Map до сих пор присутствует объект, для которого было установлено значение null