Åu ana kadar bu karmaÅık veri yapılarını gördük:
- Anahtar deÄere sahip verileri tutan objeler.
- Sıralı bir biçimde verileri tutan Diziler.
Ancak bunlar yeterli olmayabiliyorlar. Bu yüzden Map ve Set diye yapılar bulunuyor. (Collections)
Map
Map, anahtar deÄere sahip veriler tutan bir yapıdır (collection). Tıpkı Obje gibi. Fakat aralarındaki en önemli farklardan biri Mapler anahtar deÄer olarak herhangi bir tipte olabilirler.
Ana fonksiyonlar Åu Åekildedir:
new Map()â map yaratır.map.set(key, value)â Anahtara deÄer atar.map.get(key)â Anahtarın deÄerini döndürür. EÄer öyle biranahtaryoksaundefineddöndürür.map.has(key)â EÄer öyle bir anahtar varsatrueyoksafalsedöndürür.map.delete(key)â Verilen anahtara ait deÄeri siler.map.clear()â Mapin içini temizler.map.sizeâ anlık eleman sayısını döndürür.
ÃrneÄin:
let map = new Map();
map.set('1', 'str1'); // String tipinde anahtar
map.set(1, 'num1'); // Sayı tipinde anahtar
map.set(true, 'bool1'); // boolean tipinde anahtar
// sıradan Objeleri hatırlıyorsunuzdur. Anahtar deÄerleri stringe dönüÅürdü
// Map anahtar tipini de korur, tıpkı Åu 2 farklı Åekilde olduÄu gibi:
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
alert( map.size ); // 3
GördüÄümüz üzere, objelerden farklı olarak, anahtarlar stringe dönüÅmediler. Herhangi bir tipte anahtar kullanmak mümkündür.
Mapâler ayrıca anahtar olarak Obje de kullanabilir.
ÃrneÄin:
let john = { name: "John" };
// John'un ziyaret sayısını tutalım
let ziyaretSayisiMap = new Map();
// john map için anahtar olarak kullanıldı
ziyaretSayisiMap.set(john, 123);
alert( ziyaretSayisiMap.get(john) ); // 123
Nesneleri anahtar olarak kullanmak, en dikkate deÄer ve önemli Map özelliklerinden biridir. String anahtarlar için Obje yeterli olabilir fakat yukarıdaki örnek için Map yerine Obje kullanmak daha zordur.
Eskiden, Mapin olmadıÄı zamanlarda, geliÅtiriciler objelere eÅsiz tanımlayıcılar eklerdi:
// id deÄeri ekledik
let john = { name: "John", id: 1 };
let ziyaretSayisi = {};
// Åimdi id kullanarak veriyi tuttuk
ziyaretSayisi[john.id] = 123;
alert( ziyaretSayisi[john.id] ); // 123
ziyaretSayisi bir nesne olduÄundan, John gibi tüm anahtarları dizelere dönüÅtürür, bu nedenle â[object Object]â dize anahtarına sahibiz.
â¦Ama Map kullanması çok daha hoÅ.
Map anahtarları nasıl karÅılaÅtırır"
DeÄerlerin eÅitliÄini test etmek için âMapâ SameValueZero algoritmasını kullanır. Bu algoritma sıkı eÅitlik === ile kabaca aynıdır fakat farkı NaNın NaNa eÅit olmasıdır. Böylece NaN bir anahtar deÄer olarak kullanılabilir.
Bu algoritma deÄiÅtirilemez veya özelleÅtirilemez.
````"Zincirleme"
Tüm `map.set` çaÄırmaları mapin kendisini döndürür. Böylece çaÄırmaları `zincir`leyebiliriz:
```js
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
```
Objeden Map
Bir Map oluÅturduÄumuzda anahtar-deÄer çifti olarak dizi kullanabiliriz:
// [key, value] çiftlerinden oluÅan dizi
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
Tıpkı bu formatta objeler için anahtar/deÄer çifti dizisi döndüren bir yerleÅik fonksiyon Object.entries(obj) vardır.
Böylece bir objeden bir map oluÅturabiliriz:
let map = new Map(Object.entries({
name: "John",
age: 30
}));
Burada, Object.entries anahtar/deÄer çifti dizisi döndürür: [ ["name","John"], ["age", 30] ]. Mapin ihtiyacı olan da buydu.
Map üzerinde yineleme
Map üzerinde döngü yapmak için 3 metod vardır:
map.keys()â anahtarlar için bir yinelenebilir döndürür.map.values()â deÄerler için bir yinelenebilir döndürür.map.entries()â[key, value]giriÅleri için bir yinelenebilir döndürür,for..ofiçinde varsayılan olarak kullanılır.
ÃrneÄin:
let yemekMap = new Map([
['salatalik', 500],
['domates', 350],
['sogan', 50]
]);
// anahtarlar üzerinde yineleme (sebzeler)
for(let vegetable of yemekMap.keys()) {
alert(vegetable); // salatalik, domates, sogan
}
// deÄerler üzerinde yineleme (miktarlar)
for(let amount of yemekMap.values()) {
alert(amount); // 500, 350, 50
}
// [anahtar, deÄer] üzerinde yineleme
for(let entry of yemekMap) { // yemekMap.entries() ile aynı
alert(entry); // salatalik,500 (vb.)
}
Yineleme deÄerlerin eklenme sırasıyla yapıldı. Sıradan Objelerden farklı olarak Map bu sırayı korur.
Bunun yanı sıra, Map yerleÅik forEach metoduna sahiptir, tıpkı Dizi gibi:
yemekMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // salatalik: 500 vb.
});
Set
Set her deÄerin sadece birer kez olabileceÄi yapılardır (collection).
Ana fonksiyonlar Åu Åekildedir:
new Set(iterable)â set oluÅturur, isteÄe baÄlı olarak deÄerler içeren diziden de oluÅturulabilir.set.add(value)â bir deÄer ekler, setâin kendisini döndürürset.delete(value)â deÄeri siler. EÄer öyle birdeÄervarsatrueyoksafalsedöndürür.set.has(value)â EÄer öyle birdeÄervarsatrueyoksafalsedöndürür.set.clear()â setâin içindeki her Åeyi siler.set.sizeâ eleman sayısını döndürür.
ÃrneÄin, misafirlerimiz geliyor ve herkesi hatırlamak istiyoruz. Fakat ikinci defa gelenlerin tekrarlanmasını istemiyoruz. Bir ziyaretçinin sadece bir kez âsayılmasıâ gerekiyor.
Set tam olarak ihtiyacımız olan Åey:
let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// ziyaretler, bazıları birden çok kez gelmiÅ
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
// set sadece eÅsiz deÄerler tutar
alert( set.size ); // 3
for(let user of set) {
alert(user.name); // John (sonraki döngülerde Pete and Mary)
}
Kullanıcılardan oluÅan bir dizi Sete alternatif olabilir ve arr.find kullanarak her ekleme yaparken aynısından var mı diye kontrol yapabiliriz. Fakat bu kodumuzun performansını azaltır. Ãünkü bu metod ile her seferinde dizinin tüm elemanlarını kontrol etmemiz gerekir. Set eÅsizlik kontrolü yapmak için daha iyi optimize edilmiÅtir.
Set üzerinde yineleme
âforâ¦ofâ veya âforEachâ kullanarak set üzerinde yineleme yapmamız mümkündür:
let set = new Set(["portakal", "elma", "muz"]);
for(let value of set) alert(value);
// forEach ile de aynı Åekilde:
set.forEach((value, valueAgain, set) => {
alert(value);
});
Komiktir ki Set içerisindeki forEach` fonksiyonu 3 argümana sahiptir: bir deÄer, sonra tekrardan bir deÄer, ve hedef obje. Aslında aynı deÄeri argümanda 2 kez görürüz.
Bu, 3 argüman alan forEach fonksiyonuna sahip olan Map ile uyumlu olması için yapılmıÅtır.
Mapin sahip olduÄu yineleme yapan fonksiyonlar burada da vardır:
set.keys()â deÄerler için bir yinelenebilir nesne döndürür,set.values()âset.keysile aynı,Mapile uyumlu olması için yapılmıÅ,set.entries()â[value, value]giriÅleri için yinelenebilir obje döndürür,Mapile uyumlu olması için vardır.
WeakMap and WeakSet
WeakSet, JavaScriptâin WeakSetâteki ögeleri bellekten kaldırmasını engellemeyen özel bir tür Set dir. WeakMap de Map için aynı Åeydir.
Ãöp Toplama ( Garbage collection ) konusundan bildiÄimiz üzere, JavaScript motoru bir deÄeri ona eriÅebildiÄi(ve potansiyel olarak kullanılabildiÄi) sürece bellekte tutar.
ÃrneÄin:
let john = { name: "John" };
// Obje eriÅebilir, john da onun referansı
// referansın üzerine yazalım (overwrite)
john = null;
// obje daha fazla eriÅebilir olmadıÄı için bellekten silinir.
Genellikle, bir veri yapısı hafızada bulunduÄu sürece onun ögelerine (bir objenin özelliklerine veya bir dizinin elamanlarına) ulaÅılabilir ve hafızada tutulabilir kabul edilir.
Normal Mapte bir objeyi anahtar veya deÄer olarak tutmamızın bir önemi yoktur. BaÅka referansı olmasa bile bellekte tutulur.
ÃrneÄin:
let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // referansın üzerine yazalım (overwrite)
// john map içinde tutuldu
// map.keys() kullanarak ona ulaÅabiliriz
WeakMap/WeakSet istisnası olarak.
WeakMap/WeakSet nesnenin bellekten kaldırılmasını engellemez.
WeakMap ile baÅlayalım.
Mapten ilk farkı, anahtarlar obje olmalı, ilkel tipte olamaz.
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "ok"); // düzgün çalıÅır (anahtar bir obje)
weakMap.set("test", "Whoops"); // Hata verir, çünkü "test" ilkel bir tipte
Åimdi, bir nesneyi anahtar olarak kullanırsak ve o nesneye baÅka referanslar yoksa â otomatik olarak bellekten (ve mapten) kaldırılır.
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // referansın üzerine yazalım (overwrite)
// john bellekten silindi!
Yukarıdaki normal Map örneÄiyle karÅılaÅtırın. Åimdi eÄer john sadece WeakMap anahtarı olarak var olduysa â otomatik olarak silinir.
â¦Ve WeakMap keys(), values(), entries() metodlarını desteklemez, yineleme yapamayız. Bu yüzden tüm anahtar veya deÄerleri çekmemizin bir yolu yoktur.
WeakMap sadece bu metodlara sahiptir:
weakMap.get(key)weakMap.set(key, value)weakMap.delete(key, value)weakMap.has(key)
Neden böyle bir sınırlama var? Teknik sebeplerden dolayı. Nesne diÄer tüm referansları (yukarıdaki koddaki john gibi) kaybetmiÅse, otomatik olarak silinecektir. Ancak teknik olarak, temizleme iÅlemi yapılırken tam olarak belirtilmemiÅtir.
JavaScript motoru buna karar verir. Bellek temizliÄini hemen gerçekleÅtirmeyi veya sonradan daha fazla silme iÅlemi yapılana kadar beklemeyi seçebilir. Bu nedenle, teknik olarak WeakMapâin geçerli eleman sayısı bilinmemektedir. Motor onu temizlemiÅ veya temizlememiÅ, veya bunu kısmen yapmıŠolabilir. Bu nedenle, WeakMapâe bir bütün olarak eriÅen metodlar desteklenmez.
Åimdi böyle bir Åeye nerede ihtiyacımız var?
WeakMap fikri, var olan bir nesne için sadece var olduÄu sürece bir Åeyler depolayabilmemizdir. Ancak, nesneyi, onun için bir Åey depoladıÄımız gerçeÄiyle yaÅamaya zorlamıyoruz.
weakMap.put(john, "gizli belgeler");
// eÄer john ölürse, gizli belgeler yok olacak.
Objeler için bir yerde ana depolama alanına sahip olduÄumuz ve sadece obje var olduÄu sürece amacımıza yönelik ek bilgileri tutmamız gerektiÄi durumlarda kullanıÅlıdır.
Bir örnek görelim.
ÃrneÄin, her kullanıcı için ziyaret sayısını tutan bir kodumuz var. Bilgiler bir mapâte saklanır: kullanıcı anahtardır ve ziyaret sayısı deÄerdir. Bir kullanıcı ayrıldıÄında, ziyaret sayısını artık saklamak istemiyoruz.
Bunun bir yolu, terk eden kullanıcıları takip etmek ve depolamayı manuel olarak temizlemek olabilir:
let eda = { name: "Eda" };
// map: kullanıcı => ziyaret sayısı
let ziyaretSayisiMap = new Map();
// eda map için anahtardır
ziyaretSayisiMap.set(eda, 123);
// eÄer eda bizi terk ederse, ona artık ihtiyacımız yoktur. (Gerçek hayatın aksine...)
eda = null;
// Ama o hala map'te kalmaya devam eder, onu temizlememiz lazım! (Peki ya kalbimizden?)
alert( ziyaretSayisiMap.size ); // 1
// Ayrıca hafızada da durur, çünkü Map onu anahtar olarak kullanıyor.
Bir diÄer yol WeakMap kullanmak olabilir:
let eda = { name: "Eda" };
let ziyaretSayisiMap = new WeakMap();
ziyaretSayisiMap.set(eda, 123);
// eÄer eda bizi terk ederse, ona artık ihtiyacımız yoktur.
eda = null;
// WeakMap dıÅında bir referans yoktur,
// Bu yüzden obje hafıza ve ziyaretSayisiMap'ten otomatik olarak silinir.
Sıradan Map ile, bir kullanıcı ayrıldıktan sonra temizlik yapmak sıkıcı bir iÅ haline gelir: kullanıcıyı yalnızca ana depolama alanından (deÄiÅken veya dizi olsun) kaldırmamız deÄil, aynı zamanda ziyaretSayisiMap gibi ek alanları da temizlememiz gerekir. Ayrıca, kullanıcıların kodun bir yerinde yönetildiÄi ve ek yapının baÅka bir yerde olduÄu ve kaldırma iÅlemleri hakkında bilgi almadıÄı daha karmaÅık durumlarda hantal olabilir.
WeakMap iÅleri daha basit hale getirebilir, çünkü otomatik olarak temizlenir. Yukarıdaki örnekte ziyaret sayısı gibi bilgiler, yalnızca anahtar nesne var olduÄunda yaÅar.
WeakSet benzer Åekilde davranır:
Sete benzer, ancakWeakSete yalnızca nesneler ekleyebiliriz (ilkel deÄil).- Bir nesne ona baÅka bir yerden ulaÅılabildiÄi sürece set içinde var olur.
Setgibi,add,hasvedeleteyi destekler, amasize,keys()ve yinelemeleri desteklemez.
ÃrneÄin, bir ögenin kontrol edilip edilmediÄini takip etmek için kullanabiliriz:
let messages = [
{mesaj: "Merhaba", kimden: "John"},
{mesaj: "Nasıl gidiyor?", kimden: "John"},
{mesaj: "GörüÅürüz", kimden: "Alice"}
];
// dizi elemanları ile doldur (3 eleman)
let unreadSet = new WeakSet(messages);
// unreadSet'i mesajın okunup okunmadıÄını görmek için kullanabiliriz
alert(unreadSet.has(messages[1])); // true
// okuduktan sonra sil
unreadSet.delete(messages[1]); // true
// mesaj geçmiÅini kaydırdıÄımızda set otomatik olarak temizlenir
messages.shift();
// unreadSet'i temizlememize gerek yok, Åu an 2 elemanı var
// ne yazık ki, ögelerin tam sayısını elde etmek için bir yöntem yoktur, bu yüzden gösteremezsiniz
WeakMap ve WeakSetin en dikkate deÄer sınırlaması, yinelemelerin olmaması ve mevcut tüm içeriÄin alınamamasıdır. Bu rahatsız edici görünebilir, ancak aslında WeakMap / WeakSetin ana iÅlerini yapmasını engellemez â baÅka bir yerde saklanan / yönetilen nesneler için âekâ veri depolama alanı olur.
Ãzet
-
Mapâ anahtarlı deÄerler tutan bir yapıdır.(collection)normal
Objeden farkları:- Herhangi bir anahtar için objeler anahtar olabilir.
- Yinelemeler eklenme sırasıyla yapılır.
- Ek olarak kullanıÅlı metodlar,
sizeözelliÄi.
-
Setâ eÅsiz deÄerler tutan bir yapı.(collection)- Bir dizi aksine elemanların tekrar sıralanmasına izin vermez.
- Eklenme sırasıyla tutar.
-
WeakMapâ anahtar olarak sadece obje alan ve baÅka yolla ulaÅılamaz hale geldiklerinde onları silenMapin farklı bir biçimi.- Bir bütün olarak yapı üzerinde yapılan iÅlemleri desteklemez:
sizeyok,clear()yok, yineleme yok.
- Bir bütün olarak yapı üzerinde yapılan iÅlemleri desteklemez:
-
WeakSetâ sadece obje tutan ve baÅka yolla ulaÅılamaz hale geldiklerinde onları silenSetin farklı bir biçimi.- Aynı Åekilde
size/clear()ve yinelemeleri desteklemez.
- Aynı Åekilde
WeakMap ve WeakSet, âanaâ nesne depolama alanına ek olarak âikincilâ veri yapıları olarak kullanılır. Nesne, ana depolama alanından kaldırıldıÄında, yalnızca WeakMap / WeakSet içinde kalır, otomatik olarak temizlenir.
Yorumlar
<code>kullanınız, birkaç satır eklemek için ise<pre>kullanın. EÄer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)