JSON ve Veri Yapıları
Giriş — Veri Alışverişinin Ortak Dili
İki farklı ülkeden iki insan buluştuğunda, iletişim kurmak için ortak bir dile ihtiyaç duyarlar. Programlar da aynıdır — bir Python sunucusunun bir JavaScript istemcisiyle konuşması gerektiğinde, ikisinin de anlayacağı bir format gerekir. İşte JSON (JavaScript Object Notation) bu ortak dildir.
JSON, JavaScript nesne sözdiziminden esinlenerek oluşturulmuş, hafif bir veri değişim formatıdır. Bugün internetteki API'lerin büyük çoğunluğu JSON kullanır. Ayrıca bu derste JavaScript'in yerleşik veri yapıları olan Map, Set, WeakMap ve WeakSet'i öğreneceksiniz — her biri farklı problemleri çözmek için tasarlanmış özel koleksiyonlardır.
Analoji: JSON, programlar arasındaki "Esperanto" dilidir. Herkes kendi ana dilinde (Python, Java, C#, JavaScript) düşünür, ama birbirleriyle konuşmak için JSON formatına çevirir.
JSON.stringify()kendi dilinizden JSON'a çeviridir,JSON.parse()ise JSON'dan kendi dilinize çeviridir.
JSON Nedir?
JSON (JavaScript Object Notation), metin tabanlı bir veri formatıdır. JavaScript nesne sözdiziminden türemiştir ama dilden bağımsızdır — Python, Java, C#, Go... hemen hemen tüm diller JSON okuyup yazabilir.
JSON Sözdizimi Kuralları
{
"isim": "Ali Yılmaz",
"yas": 25,
"aktif": true,
"adres": {
"sehir": "İstanbul",
"postaKodu": "34710"
},
"hobiler": ["yüzme", "kodlama", "okuma"],
"telefon": null
}JSON kuralları (JavaScript nesnelerinden farkları):
| Kural | JSON | JavaScript Nesnesi |
|---|---|---|
| Anahtarlar | Çift tırnak zorunlu "isim" | Tırnaksız olabilir isim |
| String değerler | Çift tırnak zorunlu "Ali" | Tek/çift/backtick 'Ali' |
| Fonksiyonlar | ❌ Desteklemez | ✅ Destekler |
| Trailing comma | ❌ Yasak | ✅ İzin verilir |
| undefined | ❌ Desteklemez | ✅ Destekler |
| Yorumlar (comment) | ❌ Yasak | ✅ // yorum |
| Date nesnesi | ❌ String olarak | ✅ Destekler |
// ❌ Bu geçerli JSON DEĞİLDİR
// {
// isim: "Ali", // Anahtar tırnaksız
// 'yas': 25, // Tek tırnak
// hobiler: ["a", "b",], // Trailing comma
// selamla: function() {} // Fonksiyon
// }
// ✅ Geçerli JSON
// {
// "isim": "Ali",
// "yas": 25,
// "hobiler": ["a", "b"]
// }JSON.stringify() — Nesne → JSON String
JavaScript nesnesini JSON string'ine dönüştürür:
const kullanici = {
isim: "Ali",
yas: 25,
aktif: true,
hobiler: ["kodlama", "yüzme"]
};
// Temel kullanım
const jsonString = JSON.stringify(kullanici);
console.log(jsonString);
// '{"isim":"Ali","yas":25,"aktif":true,"hobiler":["kodlama","yüzme"]}'
console.log(typeof jsonString); // "string" — artık bir string!Güzel Formatlama (Pretty Print)
// İkinci parametre: replacer (null = hepsini dahil et)
// Üçüncü parametre: girinti (indent)
const guzelJson = JSON.stringify(kullanici, null, 2);
console.log(guzelJson);
// {
// "isim": "Ali",
// "yas": 25,
// "aktif": true,
// "hobiler": [
// "kodlama",
// "yüzme"
// ]
// }Replacer — Seçici Dönüşüm
const kullanici = {
isim: "Ali",
email: "ali@test.com",
sifre: "gizli123",
yas: 25
};
// Dizi replacer — sadece belirtilen alanları dahil et
const guvenli = JSON.stringify(kullanici, ["isim", "email", "yas"]);
console.log(guvenli); // '{"isim":"Ali","email":"ali@test.com","yas":25}'
// sifre dahil edilmedi!
// Fonksiyon replacer — değerleri dönüştür
const donusturulmus = JSON.stringify(kullanici, (anahtar, deger) => {
if (anahtar === "sifre") return undefined; // undefined döndürmek property'yi çıkarır
if (typeof deger === "string") return deger.toUpperCase();
return deger;
});
console.log(donusturulmus);
// '{"isim":"ALI","email":"ALI@TEST.COM","yas":25}'stringify'ın Davranışları
// undefined, fonksiyon ve Symbol değerleri atlanır
console.log(JSON.stringify({
isim: "Ali",
yas: undefined, // atlanır
selamla: function(){}, // atlanır
id: Symbol("id") // atlanır
}));
// '{"isim":"Ali"}'
// Date nesnesi string'e dönüşür
console.log(JSON.stringify({ tarih: new Date() }));
// '{"tarih":"2025-03-01T07:20:00.000Z"}'
// NaN ve Infinity → null
console.log(JSON.stringify({ a: NaN, b: Infinity }));
// '{"a":null,"b":null}'
// toJSON() metodu — özel serileştirme
const ozelNesne = {
isim: "Ali",
gizliVeri: "xxx",
toJSON() {
return { isim: this.isim }; // Sadece ismi döndür
}
};
console.log(JSON.stringify(ozelNesne));
// '{"isim":"Ali"}' — toJSON'ın döndürdüğü kullanılırJSON.parse() — JSON String → Nesne
JSON string'ini JavaScript nesnesine dönüştürür:
const jsonString = '{"isim":"Ali","yas":25,"aktif":true}';
const nesne = JSON.parse(jsonString);
console.log(nesne.isim); // "Ali"
console.log(nesne.yas); // 25
console.log(typeof nesne); // "object"Güvenli JSON Parse
// ❌ Geçersiz JSON hata fırlatır
// JSON.parse("bu geçersiz json"); // SyntaxError!
// ✅ Güvenli parse fonksiyonu
function guvenliParse(jsonString, varsayilan = null) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.warn(`JSON parse hatası: ${error.message}`);
return varsayilan;
}
}
console.log(guvenliParse('{"isim":"Ali"}')); // { isim: "Ali" }
console.log(guvenliParse('bozuk veri', {})); // {} (varsayılan)Reviver — Parse Sırasında Dönüşüm
const jsonString = '{"isim":"Ali","dogumTarihi":"1998-05-15","bakiye":"1500.50"}';
// Reviver ile tip dönüşümü
const kullanici = JSON.parse(jsonString, (anahtar, deger) => {
// Tarih alanlarını Date nesnesine dönüştür
if (anahtar === "dogumTarihi") return new Date(deger);
// Bakiyeyi number'a dönüştür
if (anahtar === "bakiye") return parseFloat(deger);
return deger;
});
console.log(kullanici.dogumTarihi instanceof Date); // true
console.log(typeof kullanici.bakiye); // "number"JSON ile Derin Kopya
// JSON ile hızlı derin kopya — basit ama sınırlı
const orijinal = {
isim: "Ali",
adres: { sehir: "İstanbul", ilce: "Kadıköy" },
notlar: [85, 90, 78]
};
const derinKopya = JSON.parse(JSON.stringify(orijinal));
derinKopya.adres.sehir = "Ankara";
derinKopya.notlar.push(95);
console.log(orijinal.adres.sehir); // "İstanbul" — değişmedi!
console.log(orijinal.notlar); // [85, 90, 78] — değişmedi!⚠️ Dikkat: JSON derin kopya yöntemi şu değerleri kaybeder:
Datenesneleri string'e dönüşürundefined, fonksiyonlar, Symbol'ler atlanırMap,Setboş nesneye dönüşürDöngüsel referanslar hata fırlatır
// ✅ Modern alternatif: structuredClone() (ES2022)
const gercekDerinKopya = structuredClone(orijinal);
// Date, Map, Set, ArrayBuffer gibi tipleri doğru kopyalar
// Ama fonksiyonları yine kopyalayamazMap — Anahtar-Değer Koleksiyonu
Map, düz nesnelere benzer ama önemli avantajları vardır:
// Map oluşturma
const harita = new Map();
// Eleman ekleme — set(anahtar, değer)
harita.set("isim", "Ali");
harita.set("yas", 25);
harita.set(42, "cevap"); // Anahtar number olabilir!
harita.set(true, "evet"); // Anahtar boolean olabilir!
const kullaniciObj = { id: 1 };
harita.set(kullaniciObj, "admin"); // Anahtar nesne bile olabilir!
// Eleman okuma — get(anahtar)
console.log(harita.get("isim")); // "Ali"
console.log(harita.get(42)); // "cevap"
console.log(harita.get(kullaniciObj)); // "admin"
// Eleman var mı? — has(anahtar)
console.log(harita.has("isim")); // true
console.log(harita.has("email")); // false
// Eleman silme — delete(anahtar)
harita.delete(true);
// Boyut
console.log(harita.size); // 4
// Tüm elemanları silme
// harita.clear();
// Constructor ile oluşturma (entries dizisi)
const renkler = new Map([
["kırmızı", "#FF0000"],
["yeşil", "#00FF00"],
["mavi", "#0000FF"]
]);Map vs Object — Ne Zaman Hangisi?
// Map'in avantajları:
// 1. Herhangi bir tip anahtar olabilir (object sadece string/symbol)
const fonksiyonMap = new Map();
fonksiyonMap.set(console.log, "logger fonksiyonu");
fonksiyonMap.set(Math.max, "max fonksiyonu");
// 2. Ekleme sırası korunur (guaranteed)
const siralı = new Map();
siralı.set("z", 1);
siralı.set("a", 2);
siralı.set("m", 3);
console.log([...siralı.keys()]); // ["z", "a", "m"] — ekleme sırası
// 3. Boyut bilgisi O(1) — .size
console.log(siralı.size); // 3
// Object: Object.keys(obj).length — O(n)
// 4. Doğrudan iterable — for...of ile dönülebilir
for (const [anahtar, deger] of renkler) {
console.log(`${anahtar}: ${deger}`);
}
// 5. Sık ekleme/silme işlemlerinde performans avantajı
// Object'in avantajları:
// - JSON ile doğrudan uyumlu
// - Literal syntax daha kısa: { a: 1 } vs new Map([["a", 1]])
// - Destructuring desteği
// - Dot notation erişimMap ile Pratik Kullanımlar
// Sayaç (frekans analizi)
function kelimeSay(metin) {
const sayac = new Map();
for (const kelime of metin.toLowerCase().split(/\s+/)) {
sayac.set(kelime, (sayac.get(kelime) || 0) + 1);
}
return sayac;
}
const sonuc = kelimeSay("bir iki bir üç bir iki");
console.log(sonuc); // Map { "bir" => 3, "iki" => 2, "üç" => 1 }
// En sık tekrar eden kelimeyi bul
const enSik = [...sonuc.entries()].sort((a, b) => b[1] - a[1])[0];
console.log(`En sık: "${enSik[0]}" (${enSik[1]} kez)`);
// Önbellek (cache)
function cachedFetch(url) {
const cache = new Map();
return async function() {
if (cache.has(url)) {
console.log("Cache'ten döndü");
return cache.get(url);
}
console.log("Sunucudan alınıyor...");
// const response = await fetch(url);
// const data = await response.json();
const data = { simule: true }; // Simüle
cache.set(url, data);
return data;
};
}💡 İpucu: JSON verisi ile çalışıyorsanız Object kullanın (JSON uyumluluğu). Dinamik anahtarlar, sık ekleme/silme veya nesne/fonksiyon anahtarlar gerekiyorsa Map kullanın.
Set — Benzersiz Değer Koleksiyonu
Set, tekrar etmeyen değerlerden oluşan bir koleksiyondur. Her değer sadece bir kez bulunabilir:
// Set oluşturma
const benzersiz = new Set();
benzersiz.add(1);
benzersiz.add(2);
benzersiz.add(3);
benzersiz.add(2); // Zaten var — eklenmez!
benzersiz.add(1); // Zaten var — eklenmez!
console.log(benzersiz); // Set { 1, 2, 3 }
console.log(benzersiz.size); // 3
// Kontrol
console.log(benzersiz.has(2)); // true
console.log(benzersiz.has(5)); // false
// Silme
benzersiz.delete(2);
console.log(benzersiz); // Set { 1, 3 }
// Diziden oluşturma
const tekrarli = [1, 2, 2, 3, 3, 3, 4, 4, 5];
const benzersizDizi = [...new Set(tekrarli)];
console.log(benzersizDizi); // [1, 2, 3, 4, 5] — tekrarlar temizlendi!
// String'den benzersiz karakterler
const kelime = "mississippi";
const benzersizHarfler = [...new Set(kelime)];
console.log(benzersizHarfler); // ["m", "i", "s", "p"]Set ile Küme İşlemleri
const a = new Set([1, 2, 3, 4, 5]);
const b = new Set([4, 5, 6, 7, 8]);
// Birleşim (Union) — her iki kümedeki tüm elemanlar
const birlesim = new Set([...a, ...b]);
console.log([...birlesim]); // [1, 2, 3, 4, 5, 6, 7, 8]
// Kesişim (Intersection) — her iki kümede ortak olanlar
const kesisim = new Set([...a].filter(x => b.has(x)));
console.log([...kesisim]); // [4, 5]
// Fark (Difference) — a'da olup b'de olmayanlar
const fark = new Set([...a].filter(x => !b.has(x)));
console.log([...fark]); // [1, 2, 3]
// ES2025'te Set metotları gelecek:
// a.union(b), a.intersection(b), a.difference(b)
// Bazı tarayıcılarda zaten kullanılabilirPratik Set Kullanımları
// Etiket sistemi
const makaleTags = new Set(["javascript", "web", "programlama"]);
makaleTags.add("javascript"); // Zaten var, tekrarlanmaz
makaleTags.add("typescript");
console.log([...makaleTags]); // ["javascript", "web", "programlama", "typescript"]
// Ziyaret edilen sayfalar (benzersiz ziyaretçi sayısı)
const ziyaretler = new Set();
function sayfaZiyaretKaydet(kullaniciId) {
ziyaretler.add(kullaniciId);
console.log(`Benzersiz ziyaretçi: ${ziyaretler.size}`);
}
sayfaZiyaretKaydet("user-1"); // Benzersiz ziyaretçi: 1
sayfaZiyaretKaydet("user-2"); // Benzersiz ziyaretçi: 2
sayfaZiyaretKaydet("user-1"); // Benzersiz ziyaretçi: 2 — tekrar!
sayfaZiyaretKaydet("user-3"); // Benzersiz ziyaretçi: 3WeakMap ve WeakSet
WeakMap ve WeakSet, Map ve Set'in "zayıf referanslı" versiyonlarıdır. Anahtarları (WeakMap) veya değerleri (WeakSet) sadece nesne olabilir ve garbage collector tarafından temizlenebilir.
WeakMap
const weakMap = new WeakMap();
let kullanici = { isim: "Ali" };
weakMap.set(kullanici, { son_giris: new Date() });
console.log(weakMap.get(kullanici)); // { son_giris: Date }
// Kullanıcı referansı silinirse, WeakMap'teki entry de garbage collect edilir
kullanici = null; // Artık kullanici nesnesine referans yok
// WeakMap'teki entry otomatik olarak temizlenecek
// WeakMap kısıtlamaları:
// - Anahtarlar sadece nesne olabilir (string, number olmaz)
// - Iterable DEĞİLDİR (for...of, .keys(), .values() yok)
// - .size özelliği yok
// - .clear() metodu yokWeakMap'in kullanım alanları:
// 1. Özel (private) veri saklama
const ozelVeri = new WeakMap();
class Kullanici {
constructor(isim, email) {
this.isim = isim;
ozelVeri.set(this, { email, girisDenemesi: 0 });
}
emailGoster() {
return ozelVeri.get(this).email;
}
girisDenemesiArtir() {
const veri = ozelVeri.get(this);
veri.girisDenemesi++;
}
}
const ali = new Kullanici("Ali", "ali@test.com");
console.log(ali.isim); // "Ali" — public
console.log(ali.emailGoster()); // "ali@test.com" — metot ile erişim
// console.log(ali.email); // undefined — doğrudan erişilemez!
// 2. DOM element'lerine veri bağlama (metadata)
// const elementVeri = new WeakMap();
// const btn = document.querySelector("#myBtn");
// elementVeri.set(btn, { tikSayisi: 0 });
// Element DOM'dan kaldırılınca WeakMap'teki veri de temizlenirWeakSet
const gorulenler = new WeakSet();
let mesaj1 = { id: 1, metin: "Merhaba" };
let mesaj2 = { id: 2, metin: "Nasılsın" };
// Mesaj görüldü olarak işaretle
gorulenler.add(mesaj1);
console.log(gorulenler.has(mesaj1)); // true
console.log(gorulenler.has(mesaj2)); // false
// Sonsuz döngü önleme (recursive traversal)
function derinYazdir(nesne, gorulenNesneler = new WeakSet()) {
if (gorulenNesneler.has(nesne)) {
console.log("[Döngüsel referans tespit edildi]");
return;
}
if (typeof nesne === "object" && nesne !== null) {
gorulenNesneler.add(nesne);
for (const [anahtar, deger] of Object.entries(nesne)) {
console.log(`${anahtar}:`);
derinYazdir(deger, gorulenNesneler);
}
} else {
console.log(` ${nesne}`);
}
}
// Döngüsel referans testi
const a = { isim: "A" };
const b = { isim: "B", ref: a };
a.ref = b; // Döngüsel referans!
derinYazdir(a);
// isim:
// A
// ref:
// isim:
// B
// ref:
// [Döngüsel referans tespit edildi]Ne Zaman Hangisini Kullanmalı?
| Veri Yapısı | Kullanım Alanı | Anahtar Tipi | Iterable |
|---|---|---|---|
| Object | Genel amaçlı, JSON uyumlu | String/Symbol | Kısmen |
| Map | Sık ekleme/silme, dinamik anahtar | Herhangi bir tip | ✅ Evet |
| Set | Benzersiz değerler, küme işlemleri | — | ✅ Evet |
| WeakMap | Nesne metadata, bellek yönetimi | Sadece Object | ❌ Hayır |
| WeakSet | Nesne takibi, döngü önleme | Sadece Object | ❌ Hayır |
// Karar ağacı:
// Veri JSON'a dönüştürülecek mi? → Object
// Anahtarlar string dışında olabilir mi? → Map
// Sadece benzersiz değerler mi? → Set
// Nesne referansı garbage collect edilmeli mi? → WeakMap/WeakSet
// Basit key-value config mi? → Object
// Dinamik, sık değişen koleksiyon mu? → Map veya SetGerçek Dünya Örneği: Caching Sistemi
// TTL (Time-To-Live) destekli cache sistemi
class Cache {
#veri = new Map(); // Ana veri deposu
#sureler = new Map(); // TTL bilgileri
set(anahtar, deger, ttlMs = 60000) {
this.#veri.set(anahtar, deger);
this.#sureler.set(anahtar, Date.now() + ttlMs);
}
get(anahtar) {
// Süre dolmuş mu kontrol et
if (this.#sureler.has(anahtar) && Date.now() > this.#sureler.get(anahtar)) {
this.delete(anahtar);
return undefined;
}
return this.#veri.get(anahtar);
}
has(anahtar) {
return this.get(anahtar) !== undefined;
}
delete(anahtar) {
this.#veri.delete(anahtar);
this.#sureler.delete(anahtar);
}
clear() {
this.#veri.clear();
this.#sureler.clear();
}
get size() {
// Süresi dolmuşları temizle ve gerçek boyutu döndür
for (const [anahtar, sure] of this.#sureler) {
if (Date.now() > sure) {
this.delete(anahtar);
}
}
return this.#veri.size;
}
istatistik() {
return {
toplamKayit: this.size,
anahtarlar: [...this.#veri.keys()]
};
}
}
// Kullanım
const cache = new Cache();
cache.set("kullanici:1", { isim: "Ali", yas: 25 }, 5000); // 5 saniye
cache.set("ayarlar", { tema: "koyu" }, 60000); // 1 dakika
console.log(cache.get("kullanici:1")); // { isim: "Ali", yas: 25 }
console.log(cache.size); // 2
console.log(cache.istatistik());Özet
🔹 JSON metin tabanlı, dilden bağımsız bir veri formatıdır.
JSON.stringify()nesne → string,JSON.parse()string → nesne dönüşümü yapar🔹 JSON'da anahtarlar ve string değerler çift tırnak zorunludur;
undefined, fonksiyon veSymboldesteklenmez🔹 Map herhangi bir tipte anahtar destekler, ekleme sırasını korur ve sık ekleme/silme işlemlerinde performans avantajı sunar
🔹 Set benzersiz değerler koleksiyonudur — dizi tekrarlarını temizlemek (
[...new Set(dizi)]) ve küme işlemleri (birleşim, kesişim, fark) için idealdir🔹 WeakMap/WeakSet zayıf referans tutar — nesne başka yerden referanslanmadığında garbage collect edilmesine izin verir; metadata ve döngü önleme için kullanılır
🔹 JSON uyumluluğu gerekiyorsa Object, dinamik/nesne anahtarlar gerekiyorsa Map, benzersiz koleksiyon gerekiyorsa Set tercih edin
AI Asistan
Sorularını yanıtlamaya hazır