Loading...

WeakMap жана WeakSet

WeakMap жана WeakSet

 

Таштандыларды чогултуу бөлүмүнөн билгенибиздей , JavaScript кыймылдаткычы баалуулуктарды эстутумда алар жетүүгө мүмкүн болгон учурда сактайт (башкача айтканда, бул баалуулуктарды колдонсо болот).

Мисалы:

@A@let john = { name: "John" };

// объект доступен, переменная john -- это ссылка на него

// перепишем ссылку
john = null;

// объект будет удалён из памяти@A@

Адатта, объекттин касиеттери, массивдин элементтери же башка маалымат структуралары жеткиликтүү болуп эсептелет жана ал маалымат структурасы эстутумда камтылганга чейин эстутумда сакталат.

Мисалы, объектти массивге койсок, массив бар болгонго чейин, объект дагы ага шилтемелер болбогонуна карабастан, эс тутумда да болот.

Мисалы:

@A@let john = { name: "John" };

let array = [ john ];

john = null; // перезаписываем ссылку на объект

// объект john хранится в массиве, поэтому он не будет удалён сборщиком мусора
// мы можем взять его значение как array[0]@A@

Ошо сыяктуу эле, эгерде биз объектти ичинде ачкыч катары колдонсок Map, анда бар болгонго чейин Mapбул объект да бар болот. Ал эс тутумда орун ээлейт жана аны таштанды жыйноочу алып салууга болбойт.

Мисалы:

@A@let john = { name: "John" };

let map = new Map();
map.set(john, "...");

john = null; // перезаписываем ссылку на объект

// объект john сохранён внутри объекта `Map`,
// он доступен через map.keys()@A@

WeakMap- бул аспектиде принципиалдуу башка структура. Бул объекттер ачкычтын ролун аткарганда, объекттердин таштанды болуп калышына тоскоол болбойт.

Келгиле, бул эмнени билдирерин мисалдар менен карап көрөлү.

WeakMap

Анын биринчи айырмасы, Mapачкычтар WeakMapпримитивдүү маанилер эмес, объекттер болушу керек:

@A@let weakMap = new WeakMap();

let obj = {};

weakMap.set(obj, "ok"); // работает (объект в качестве ключа)

// нельзя использовать строку в качестве ключа
weakMap.set("test", "Whoops"); // Ошибка, потому что "test" не объект@A@

Эми, эгерде биз объектти ачкыч катары колдонсок жана бул объектке шилтемелер жок болсо, анда ал эстутумдан (жана объекттен WeakMap) автоматтык түрдө өчүрүлөт.

@A@let john = { name: "John" };

let weakMap = new WeakMap();
weakMap.set(john, "...");

john = null; // перезаписываем ссылку на объект

// объект john удалён из памяти!@A@

MapБул жүрүм-турумду мурда мисал келтирилген кадимки жүрүм-туруму менен салыштырыңыз . Азыр johnал ачкыч катары гана бар WeakMapжана ал жерден автоматтык түрдө алынып салынышы мүмкүн.

WeakMapkeys(), , итерациясын жана ыкмаларын колдобойт values()entries()андыктан андан бардык ачкычтарды же баалуулуктарды алууга эч кандай жол жок.

Төмөнкү ыкмалар гана бар WeakMap:

  • weakMap.get(key)
  • weakMap.set(key, value)
  • weakMap.delete(key)
  • weakMap.has(key)

Эмне үчүн мындай чектөөлөр? Техникалык ишке ашыруунун өзгөчөлүгүнө байланыштуу. Эгер объект жеткиликсиз болуп калса ( johnжогорку мисалдагы объект сыяктуу), анда ал таштанды жыйноочу тарабынан автоматтык түрдө жок кылынат. Бирок бул тазалоо качан болоору тууралуу маалымат жок .

Таштанды качан чогултуу керек экенин JavaScript кыймылдаткычы чечет. Бул объектти азыр эле жок кылууну же кийинчерээк көбүрөөк объекттерди жок кылуу үчүн операцияны кечиктирүүнү талап кылышы мүмкүн. Ошентип, техникалык жактан коллекциядагы элементтердин саны WeakMapбелгисиз. кыймылдаткыч дароо же кийинчерээк тазалап, же жарым-жартылай кыла алат. Ушул себептен улам, бир эле учурда бардык ачкычтарга/баалуулуктарга жетүү ыкмалары жеткиликтүү эмес.

Бирок бизге мындай маалымат структурасы эмне үчүн керек?

Мисалы: кошумча маалыматтар

Ал негизинен кошумча маалымат сактагычWeakMap катары колдонулат .

Эгер биз башка кодго, балким, үчүнчү тараптын китепканасына "тиешелүү" объект менен иштеп жаткан болсок жана биз ал үчүн ушул объект бар болгон учурда гана болушу керек болгон кээ бир маалыматтарды сактап калгыбыз келсе, анда бизге дал ушул нерсе керек. WeakMap.

WeakMapБиз бул маалыматтарды объектти ачкыч катары колдонуу менен киргизебиз жана таштанды жыйноочу объекттерди эс тутумдан алып салганда, алар менен байланышкан маалыматтар да автоматтык түрдө жок болот.

@A@weakMap.set(john, "секретные документы");
// если john умрёт, "секретные документы" будут автоматически уничтожены@A@

Келгиле, бир мисал карап көрөлү.

Бизде колдонуучулардын зыяраттарын көзөмөлдөгөн код бар дейли. Маалымат коллекцияда сакталат Map: колдонуучуну билдирген объект ачкыч, ал эми зыяраттардын саны - маани. Колдонуучу бизди таштап кеткенде (анын объектиси таштанды жыйноочу тарабынан алынып салынат), андан кийин тиешелүү зыярат эсептегичти сактоонун мааниси жок.

Бул жерде хит эсептегичти колдонуунун мисалы Map:

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

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

Жана бул жерде коддун дагы бир бөлүгү, балким башка файлда, ал колдонот countUser:

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

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

// пользователь покинул нас
john = null;@A@

Объект азыр johnташтанды чогултулушу керек, бирок ал эстутумда калат, анткени ал visitsCountMap.

Колдонуучу объекти жок кылынганда тазалашыбыз керек visitsCountMap, антпесе коллекция чексиз өсөт. Мындай тазалоо татаал колдонмо архитектурасы менен ишке ашыруу үчүн ыңгайсыз болушу мүмкүн.

Төмөнкүлөрдү колдонуу менен көйгөйлөрдөн качууга болот WeakMap:

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

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

Эми кол менен тазалоонун кереги жок visitsCountMap. Объект johnаркылуу эмес, башка жолдор менен жетүүгө мүмкүн болбой калгандан кийин WeakMap, ал эстутумдан ошол ачкыч тууралуу маалымат менен бирге өчүрүлөт WeakMap.

Кэштөө үчүн колдонмо

Колдонмонун дагы бир кеңири таралган чөйрөсү кэштөө болуп саналат, анда функциянын чалуусунун натыйжасы бир жерде сакталышы керек («кэш»), анын бир эле объектке андан аркы чалуулары аны кайра колдонуу менен мурунтан эле даярдалган натыйжаны кабыл алышы үчүн.

Натыйжаларды сактоо үчүн биз Mapтөмөнкүдөй колдоно алабыз:

@A@// 📁 cache.js
let cache = new Map();

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

    cache.set(obj, result);
  }

  return cache.get(obj);
}

// Теперь используем process() в другом файле:

// 📁 main.js
let obj = {/* допустим, у нас есть какой-то объект */};

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

// ...позже, из другого места в коде...
let result2 = process(obj); // ранее вычисленный результат взят из кеша

// ...позже, когда объект больше не нужен:
obj = null;

alert(cache.size); // 1 (Упс! Объект всё ещё в кеше, занимает память!)@A@

Аргумент катары бир эле объект менен бир нече чалуулар process(obj)натыйжаны биринчи жолу гана баалоого алып келет, андан кийин кийинки чалуулар аны кэштен алат. cacheКемчилиги – керексиз болуп калган объектилерди кол менен тазалоо зарыл .

WeakMapБирок анын ордуна колдонсок Map, анда бул көйгөй жок болот: кэштелген натыйжалар таштанды жыйноочу тарабынан эстутумдан автоматтык түрдө алынып салынат.

 
 
@A@// 📁 cache.js
let cache = new WeakMap();

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

    cache.set(obj, result);
  }

  return cache.get(obj);
}

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

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

// ...позже, когда объект больше не нужен:
obj = null;

// Нет возможности получить cache.size, так как это WeakMap,
// но он равен 0 или скоро будет равен 0
// Когда сборщик мусора удаляет obj, связанные с ним данные из кеша тоже удаляются@A@

Алсыз топтом

Коллекция WeakSetтөмөнкүдөй иштейт:

  • Бул окшош Set, бирок биз объекттерге гана кошо алабыз WeakSet(примитивдүү маанилерге эмес).
  • Объект башка жерде жеткиликтүү болгондо гана топтомдо болот.
  • жана сыяктуу Set, ал колдойт addhasжана delete, бирок жок sizekeys()жана кайталанбайт.

Баштапкы маалымат түзүмүнүн "алсыз" версиясы болуп, ал кошумча сактагыч катары да кызмат кылат. Бирок ыктыярдуу маалыматтар үчүн эмес, ооба/жок маанилери үчүн. Көптөгөн адамдардын болушу WeakSetбизге объект жөнүндө бир нерсе айтып бере алат.

WeakSetМисалы, биздин сайтка кимдер киргенине көз салуу үчүн колдонуучуларды кошо алабыз :

@A@let visitedSet = new WeakSet();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

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

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

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

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

john = null;

// структура данных visitedSet будет очищена автоматически (объект john будет удалён из visitedSet)@A@

Эң маанилүү чектөө WeakMapWeakSetаларды кайталап же бүтүндөй мазмунду алуу мүмкүн эмес. Бул ыңгайсыз болушу мүмкүн, бирок бул алардын негизги милдетине тоскоол болбойт WeakMap/WeakSet- коддун башка жерлеринен башкарылган объекттер үчүн кошумча маалымат дүкөнү болуу.

Бардыгы

WeakMapMapобъекттерди гана ачкыч катары колдонууга мүмкүндүк берген жана башка жол менен жеткиликсиз болуп калганда, аларды тиешелүү маанилери менен бирге автоматтык түрдө алып салган -like жыйнагы .

WeakSetSetобъекттерди гана сактаган жана башка жол менен жеткиликсиз болуп калганда аларды жок кылган -like коллекциясы .

Бул эки маалымат структуралары бир эле учурда бардык мазмунда иштеген же коллекциянын көлөмү жөнүндө маалыматты кайтарган ыкмаларды жана касиеттерди колдобойт. Коллекциянын бир элементи боюнча гана операцияларды жасоого болот.

WeakMapжана WeakSet"негизги" объект сактагычтын ордуна кошумча маалымат структуралары катары колдонулат. WeakMapЭгерде объект негизги сактагычтан жок кылынса жана анын ичинде же ичиндеги ачкычтан башка эч жерде колдонулбаса WeakSet, анда ал автоматтык түрдө жок кылынат.

Tasks

маанилүүлүгү: 5

бир катар билдирүүлөр бар:

@A@let messages = [
  {text: "Hello", from: "John"},
  {text: "How goes?", from: "John"},
  {text: "See you soon", from: "Alice"}
];@A@

Сиз аларга кирүү мүмкүнчүлүгүңүз бар, бирок бул массивди башкаруу башка жерде болот. Жаңы билдирүүлөр кошулуп, эскилери өчүрүлөт жана бул качан болоорун билбейсиз.

Бул маалымат менен, "билдирүү окулдубу?" деген суроого жооп берүү үчүн кайсы маалымат структурасын колдоно аларыңызды чечиңиз. Бул билдирүү ар бир билдирүү объектиси үчүн окулган-окулган-окулган-окулбаганын ачык айтууга мүмкүн болушу үчүн структура ылайыктуу болушу керек.

PS Кабар массивден алынып салынганда messages, ал маалымат структурасынан да жок болушу керек.

PPS Биз билдирүү объектилерин өздөрү өзгөртпөшүбүз керек, ал жакка касиеттерди кошушубуз керек. Эгер билдирүүлөр башка кодго таандык болсо, анда бул жаман кесепеттерге алып келиши мүмкүн.

чечим
маанилүүлүгү: 5

Мурунку тапшырмадагыдай эле бир катар билдирүүлөр бар .

@A@let messages = [
  { text: "Hello", from: "John" },
  { text: "How goes?", from: "John" },
  { text: "See you soon", from: "Alice" }
];@A@

Эми суроо туулат: билдирүү качан окулганы тууралуу маалыматты сактоо үчүн кайсы маалымат структурасын колдонууну сунуштайт элеңиз?

Мурунку тапшырмада "ооба же жок" дегенди окуу фактысын гана сактоо керек болчу. Эми биз датаны сакташыбыз керек жана ал билдирүү "таштанды жыйноочу" тарабынан алынып салынганда эстутумдан жок болушу керек.

JavaScript'теги PS Даталары класстын ичине орнотулган объекти катары сакталышы мүмкүн Date, биз аларды кийинчерээк карайбыз.