← Kursa Dön
📄 Text · 30 min

Nesneler (Objects)

Giriş — Gerçek Dünyayı Modelleme

Diziler sıralı veri koleksiyonları için harikadır, ama gerçek dünyayı tam olarak modelleyemezler. Bir kullanıcıyı düşünün — ismi, yaşı, e-postası, adresi, tercihleri var. Bu bilgileri bir dizide saklarsanız hangi index'in neye karşılık geldiğini ezberlemek zorundasınız. İşte nesneler (objects) bu sorunu çözer: veriye isimle erişmenizi sağlar.

JavaScript'te nesneler dilin kalbidir. "JavaScript'te her şey bir nesnedir" derler — tamamen doğru olmasa da (primitive'ler nesne değildir), nesneler JavaScript'in en temel yapı taşıdır. DOM, event'ler, API yanıtları, modüller — hepsi nesne tabanlıdır. Bu derste nesnelerin nasıl oluşturulduğunu, nasıl manipüle edileceğini ve modern JavaScript'in sunduğu güçlü nesne araçlarını öğreneceksiniz.

Analoji: Bir nesne, etiketli çekmeceli bir dolap gibidir. Her çekmecenin üzerinde bir etiket (property adı) vardır ve içinde bir değer bulunur. "isim" çekmecesini açınca "Ali", "yaş" çekmecesini açınca "25" bulursunuz. Dizi bir numaralı posta kutusuysa, nesne isimli bir dosya dolabıdır.


Nesne Oluşturma

Object Literal (En Yaygın)

// Boş nesne
const bosNesne = {};

// Özellikli nesne
const kullanici = {
  isim: "Ali Yılmaz",
  yas: 25,
  email: "ali@example.com",
  aktif: true,
  hobiler: ["yüzme", "kodlama", "okuma"],
  adres: {
    sehir: "İstanbul",
    ilce: "Kadıköy",
    postaKodu: "34710"
  }
};

console.log(kullanici.isim);          // "Ali Yılmaz"
console.log(kullanici.adres.sehir);   // "İstanbul"
console.log(kullanici.hobiler[1]);    // "kodlama"

Shorthand Property Names (Kısayol)

Değişken adı ve property adı aynıysa kısaltabilirsiniz:

const isim = "Ayşe";
const yas = 30;
const sehir = "Ankara";

// ❌ Eski yöntem — tekrar
const kullanici1 = { isim: isim, yas: yas, sehir: sehir };

// ✅ Shorthand — kısa ve temiz
const kullanici2 = { isim, yas, sehir };
console.log(kullanici2); // { isim: "Ayşe", yas: 30, sehir: "Ankara" }

Shorthand Methods

// ❌ Eski yöntem
const hesap1 = {
  bakiye: 1000,
  paraCek: function(miktar) {
    this.bakiye -= miktar;
  }
};

// ✅ Shorthand metot — daha kısa
const hesap2 = {
  bakiye: 1000,
  paraCek(miktar) {
    this.bakiye -= miktar;
  },
  bakiyeGor() {
    return `${this.bakiye} TL`;
  }
};

Computed Property Names (Hesaplanmış İsimler)

Property isimlerini dinamik olarak belirleyebilirsiniz:

const alan = "email";
const deger = "ali@test.com";

const kullanici = {
  isim: "Ali",
  [alan]: deger, // computed property: email: "ali@test.com"
};
console.log(kullanici.email); // "ali@test.com"

// Dinamik anahtar oluşturma
function nesneOlustur(anahtar, deger) {
  return { [anahtar]: deger };
}

console.log(nesneOlustur("renk", "mavi")); // { renk: "mavi" }
console.log(nesneOlustur("boyut", "XL"));  // { boyut: "XL" }

// Template literal ile
const onEk = "kullanici";
const ayarlar = {
  [`${onEk}Adi`]: "ali_dev",
  [`${onEk}Rolu`]: "admin",
  [`${onEk}Durumu`]: "aktif"
};
console.log(ayarlar); // { kullaniciAdi: "ali_dev", kullaniciRolu: "admin", ... }

Property Erişimi

İki yol vardır: dot notation (nokta) ve bracket notation (köşeli parantez):

const araba = {
  marka: "Toyota",
  model: "Corolla",
  yil: 2023,
  "renk kodu": "#FF0000" // Boşluklu property ismi
};

// Dot notation — standart, okunabilir
console.log(araba.marka);  // "Toyota"
console.log(araba.model);  // "Corolla"

// Bracket notation — dinamik erişim, özel karakterli isimler
console.log(araba["marka"]);      // "Toyota"
console.log(araba["renk kodu"]);  // "#FF0000" — boşluklu isimde zorunlu

// Dinamik property erişimi
const alan = "yil";
console.log(araba[alan]); // 2023

// Var olmayan property — undefined döner (hata değil)
console.log(araba.motor); // undefined

Optional Chaining (?.)

const kullanici = {
  isim: "Ali",
  adres: {
    sehir: "İstanbul"
  }
};

// ❌ Derin erişimde hata riski
// console.log(kullanici.iletisim.telefon); // TypeError!

// ✅ Optional chaining ile güvenli erişim
console.log(kullanici?.iletisim?.telefon); // undefined — hata yok
console.log(kullanici?.adres?.sehir);      // "İstanbul"

// ?? ile birleştirerek varsayılan değer
const telefon = kullanici?.iletisim?.telefon ?? "Belirtilmemiş";
console.log(telefon); // "Belirtilmemiş"

Property Ekleme, Güncelleme, Silme

const kullanici = { isim: "Ali" };

// Ekleme — yeni property atama
kullanici.yas = 25;
kullanici.email = "ali@test.com";
kullanici["telefon"] = "555-1234";

console.log(kullanici);
// { isim: "Ali", yas: 25, email: "ali@test.com", telefon: "555-1234" }

// Güncelleme — mevcut property'ye yeni değer atama
kullanici.yas = 26;
console.log(kullanici.yas); // 26

// Silme — delete operatörü
delete kullanici.telefon;
console.log(kullanici.telefon); // undefined — artık yok

// Property var mı kontrolü
console.log("isim" in kullanici);    // true
console.log("telefon" in kullanici);  // false

// hasOwnProperty — sadece kendi özelliklerini kontrol eder
console.log(kullanici.hasOwnProperty("isim")); // true
console.log(kullanici.hasOwnProperty("toString")); // false — prototype'tan

⚠️ Dikkat: const ile tanımlanan nesnenin property'leri değiştirilebilir — const sadece değişkenin yeniden atamasını engeller, nesnenin içeriğini değil. Nesneyi tamamen dondurmak için Object.freeze() kullanın:

const sabit = Object.freeze({ isim: "Ali", yas: 25 });
sabit.yas = 30;           // Sessizce görmezden gelinir (strict mode'da hata)
console.log(sabit.yas);   // 25 — değişmedi

// Object.freeze yüzeyseldir — iç nesneler hâlâ değiştirilebilir
const derin = Object.freeze({ adres: { sehir: "İstanbul" } });
derin.adres.sehir = "Ankara"; // Bu çalışır!
console.log(derin.adres.sehir); // "Ankara"

Object Destructuring (Yapı Bozma) — ES6

Nesne özelliklerini tek tek değişkenlere atamayı kolaylaştırır:

const kullanici = {
  isim: "Ali",
  yas: 25,
  email: "ali@test.com",
  sehir: "İstanbul",
  rol: "admin"
};

// Temel destructuring
const { isim, yas, email } = kullanici;
console.log(isim);  // "Ali"
console.log(yas);   // 25
console.log(email); // "ali@test.com"

// Yeniden isimlendirme (renaming)
const { isim: kullaniciAdi, yas: kullaniciYasi } = kullanici;
console.log(kullaniciAdi);  // "Ali"
console.log(kullaniciYasi); // 25

// Varsayılan değer
const { telefon = "Belirtilmemiş", sehir = "Bilinmiyor" } = kullanici;
console.log(telefon); // "Belirtilmemiş" — property yok, varsayılan kullanıldı
console.log(sehir);   // "İstanbul" — property var, kendi değeri

// İç içe destructuring
const siparis = {
  id: 1001,
  musteri: {
    isim: "Ali",
    adres: {
      sehir: "İstanbul",
      ilce: "Kadıköy"
    }
  },
  urunler: ["Laptop", "Mouse"]
};

const {
  musteri: {
    isim: musteriIsmi,
    adres: { sehir: musteriSehri }
  },
  urunler: [ilkUrun]
} = siparis;

console.log(musteriIsmi);   // "Ali"
console.log(musteriSehri);  // "İstanbul"
console.log(ilkUrun);       // "Laptop"

Fonksiyon Parametrelerinde Destructuring

// ❌ Parametreleri sırayla geçirmek — hangi argüman hangisi?
function kullaniciKarti(isim, yas, email, sehir, rol) {
  // ...
}
kullaniciKarti("Ali", 25, "ali@test.com", "İstanbul", "admin");

// ✅ Nesne parametresi ile destructuring
function kullaniciKarti({ isim, yas, email, sehir = "Belirtilmemiş", rol = "user" }) {
  return `${isim} (${yas}) — ${email} — ${sehir} [${rol}]`;
}

console.log(kullaniciKarti({
  isim: "Ali",
  yas: 25,
  email: "ali@test.com",
  rol: "admin"
  // sehir belirtilmedi — varsayılan kullanılacak
}));
// "Ali (25) — ali@test.com — Belirtilmemiş [admin]"

Spread ve Rest ile Nesneler — ES6

Spread (...) — Nesne Yayma

// Yüzeysel kopya (shallow copy)
const orijinal = { isim: "Ali", yas: 25 };
const kopya = { ...orijinal };
kopya.yas = 30;
console.log(orijinal.yas); // 25 — değişmedi

// Nesneleri birleştirme (merge)
const varsayilan = { tema: "açık", dil: "tr", bildirim: true };
const kullaniciAyar = { tema: "koyu", fontSize: 16 };
const sonAyar = { ...varsayilan, ...kullaniciAyar };
console.log(sonAyar);
// { tema: "koyu", dil: "tr", bildirim: true, fontSize: 16 }
// Aynı property'lerde sonraki kazanır (tema: "koyu")

// Tek bir property'yi güncelleyerek yeni nesne oluşturma (immutable update)
const kullanici = { isim: "Ali", yas: 25, aktif: true };
const guncellenmis = { ...kullanici, yas: 26, email: "ali@test.com" };
console.log(guncellenmis);
// { isim: "Ali", yas: 26, aktif: true, email: "ali@test.com" }

// Koşullu property ekleme
const rol = "admin";
const menu = {
  anaSayfa: true,
  profil: true,
  ...(rol === "admin" && { adminPanel: true, kullaniciYonetimi: true })
};
console.log(menu);
// { anaSayfa: true, profil: true, adminPanel: true, kullaniciYonetimi: true }

Rest (...) — Kalan Özellikleri Toplama

// Bazı property'leri ayırıp geri kalanını toplama
const kullanici = { isim: "Ali", yas: 25, email: "ali@test.com", sifre: "gizli" };

const { sifre, ...guvenliVeri } = kullanici;
console.log(guvenliVeri); // { isim: "Ali", yas: 25, email: "ali@test.com" }
// sifre çıkarıldı — API response'tan hassas veriyi ayırma

// Belirli alanları hariç tutma fonksiyonu
function haricTut(nesne, ...anahtarlar) {
  const kopya = { ...nesne };
  for (const anahtar of anahtarlar) {
    delete kopya[anahtar];
  }
  return kopya;
}

const temizVeri = haricTut(kullanici, "sifre", "email");
console.log(temizVeri); // { isim: "Ali", yas: 25 }

💡 İpucu: Spread ile nesne kopyalama yüzeyseldir (shallow). İç içe nesneler referans olarak kopyalanır. Derin kopya için structuredClone() kullanın.


Object Metotları

JavaScript'in Object sınıfı, nesneler üzerinde çalışmak için güçlü statik metotlar sunar:

Object.keys(), Object.values(), Object.entries()

const urun = {
  isim: "Laptop",
  fiyat: 15000,
  marka: "Apple",
  stok: 5
};

// Anahtarları dizi olarak al
console.log(Object.keys(urun));
// ["isim", "fiyat", "marka", "stok"]

// Değerleri dizi olarak al
console.log(Object.values(urun));
// ["Laptop", 15000, "Apple", 5]

// Anahtar-değer çiftlerini dizi olarak al
console.log(Object.entries(urun));
// [["isim", "Laptop"], ["fiyat", 15000], ["marka", "Apple"], ["stok", 5]]

// for...of ile iterasyon
for (const [anahtar, deger] of Object.entries(urun)) {
  console.log(`${anahtar}: ${deger}`);
}

// entries ile nesneyi dönüştürme
const buyukHarfli = Object.fromEntries(
  Object.entries(urun).map(([k, v]) => [k.toUpperCase(), v])
);
console.log(buyukHarfli);
// { ISIM: "Laptop", FIYAT: 15000, MARKA: "Apple", STOK: 5 }

Object.assign()

// Nesneleri birleştirme (spread öncesi yaygındı)
const hedef = { a: 1, b: 2 };
const kaynak = { b: 3, c: 4 };

Object.assign(hedef, kaynak);
console.log(hedef); // { a: 1, b: 3, c: 4 } — hedef değişir!

// Yeni nesne oluşturmak için boş hedef kullanın
const birlesmis = Object.assign({}, hedef, kaynak);
// Ama spread daha modern ve okunabilir: { ...hedef, ...kaynak }

Object.freeze() ve Object.seal()

// freeze — hiçbir şey değişmez (ekleme, silme, güncelleme)
const sabit = Object.freeze({ isim: "Ali", yas: 25 });
sabit.yas = 30;         // Sessiz hata (strict mode'da TypeError)
sabit.yeni = "prop";    // Sessiz hata
delete sabit.isim;      // Sessiz hata
console.log(sabit);     // { isim: "Ali", yas: 25 } — hiçbir şey değişmedi

// seal — güncelleme yapılabilir ama ekleme/silme yapılamaz
const muhurlu = Object.seal({ isim: "Ali", yas: 25 });
muhurlu.yas = 30;       // ✅ Çalışır
muhurlu.yeni = "prop";  // ❌ Sessiz hata — ekleme yapılamaz
delete muhurlu.isim;    // ❌ Sessiz hata — silme yapılamaz
console.log(muhurlu);   // { isim: "Ali", yas: 30 }

// Kontrol metotları
console.log(Object.isFrozen(sabit));   // true
console.log(Object.isSealed(muhurlu)); // true

Object.fromEntries()

// entries dizisinden nesne oluşturma (Object.entries'in tersi)
const entries = [["isim", "Ali"], ["yas", 25], ["sehir", "İstanbul"]];
const nesne = Object.fromEntries(entries);
console.log(nesne); // { isim: "Ali", yas: 25, sehir: "İstanbul" }

// Map'ten nesne oluşturma
const map = new Map([["a", 1], ["b", 2]]);
const nesne2 = Object.fromEntries(map);
console.log(nesne2); // { a: 1, b: 2 }

// URL search params'tan nesne
// const params = new URLSearchParams("isim=Ali&yas=25");
// const nesne3 = Object.fromEntries(params);
// { isim: "Ali", yas: "25" }

Gerçek Dünya Örneği: Ayar Yöneticisi

// Derin birleştirme (deep merge) fonksiyonu
function deepMerge(hedef, ...kaynaklar) {
  for (const kaynak of kaynaklar) {
    for (const [anahtar, deger] of Object.entries(kaynak)) {
      if (deger && typeof deger === "object" && !Array.isArray(deger)) {
        hedef[anahtar] = deepMerge(hedef[anahtar] || {}, deger);
      } else {
        hedef[anahtar] = deger;
      }
    }
  }
  return hedef;
}

// Uygulama ayar yöneticisi
function ayarYoneticisiOlustur(varsayilanAyarlar) {
  let ayarlar = structuredClone(varsayilanAyarlar);

  return {
    // Ayar oku (derin erişim: "tema.renkler.birincil")
    al(yol) {
      return yol.split(".").reduce((obj, anahtar) =>
        obj?.[anahtar], ayarlar
      );
    },

    // Ayar güncelle
    ayarla(yol, deger) {
      const anahtarlar = yol.split(".");
      const sonAnahtar = anahtarlar.pop();
      const hedef = anahtarlar.reduce((obj, anahtar) => {
        if (!obj[anahtar]) obj[anahtar] = {};
        return obj[anahtar];
      }, ayarlar);
      hedef[sonAnahtar] = deger;
    },

    // Toplu güncelleme
    birlestir(yeniAyarlar) {
      ayarlar = deepMerge(structuredClone(ayarlar), yeniAyarlar);
    },

    // Tüm ayarları döndür (kopya)
    hepsiniAl() {
      return structuredClone(ayarlar);
    },

    // Varsayılana sıfırla
    sifirla() {
      ayarlar = structuredClone(varsayilanAyarlar);
    }
  };
}

// Kullanım
const yonetici = ayarYoneticisiOlustur({
  tema: {
    mod: "açık",
    renkler: { birincil: "#2196F3", ikincil: "#FF9800" }
  },
  bildirimler: { email: true, push: false },
  dil: "tr"
});

console.log(yonetici.al("tema.mod"));              // "açık"
console.log(yonetici.al("tema.renkler.birincil"));  // "#2196F3"

yonetici.ayarla("tema.mod", "koyu");
console.log(yonetici.al("tema.mod"));               // "koyu"

yonetici.birlestir({ bildirimler: { push: true }, yeni: "ayar" });
console.log(yonetici.hepsiniAl());

Bu örnekte Object.entries, spread, destructuring, optional chaining, closure ve recursive fonksiyonlar bir arada kullanıldı.


Nesne İterasyon Yöntemleri Karşılaştırması

const kullanici = { isim: "Ali", yas: 25, sehir: "İstanbul" };

// 1. for...in — tüm enumerable property'ler (prototype dahil!)
for (const anahtar in kullanici) {
  if (kullanici.hasOwnProperty(anahtar)) { // prototype'ı filtrele
    console.log(`${anahtar}: ${kullanici[anahtar]}`);
  }
}

// 2. Object.keys() + forEach — sadece kendi property'leri
Object.keys(kullanici).forEach(anahtar => {
  console.log(`${anahtar}: ${kullanici[anahtar]}`);
});

// 3. Object.entries() + for...of — en modern ve okunabilir
for (const [anahtar, deger] of Object.entries(kullanici)) {
  console.log(`${anahtar}: ${deger}`);
}

⚠️ Dikkat: for...in prototype zincirindeki property'leri de döndürebilir. Her zaman hasOwnProperty kontrolü yapın veya daha güvenli olan Object.keys/values/entries kullanın.


Property Descriptors (Özellik Tanımlayıcıları)

Her nesne property'sinin görünmez ayarları vardır. Bu ayarlar property'nin davranışını kontrol eder:

const kullanici = { isim: "Ali", yas: 25 };

// Property descriptor'ı oku
console.log(Object.getOwnPropertyDescriptor(kullanici, "isim"));
// {
//   value: "Ali",
//   writable: true,     ← Değiştirilebilir mi?
//   enumerable: true,   ← for...in/Object.keys'de görünür mü?
//   configurable: true  ← Silinebilir/ayarlanabilir mi?
// }

// Özel property tanımlama
Object.defineProperty(kullanici, "id", {
  value: 1,
  writable: false,     // Değiştirilemez
  enumerable: false,   // Döngülerde görünmez
  configurable: false, // Silinemez, ayar değiştirilemez
});

kullanici.id = 999;         // Sessiz hata (strict: TypeError)
console.log(kullanici.id);  // 1 — değişmedi!
console.log(Object.keys(kullanici)); // ["isim", "yas"] — id görünmez

// Getter/Setter property
const kisi = {
  _isim: "Ali",
  _soyisim: "Yılmaz",
};

Object.defineProperty(kisi, "tamIsim", {
  get() {
    return `${this._isim} ${this._soyisim}`;
  },
  set(deger) {
    const [isim, soyisim] = deger.split(" ");
    this._isim = isim;
    this._soyisim = soyisim;
  },
  enumerable: true,
});

console.log(kisi.tamIsim);      // "Ali Yılmaz" — getter
kisi.tamIsim = "Ayşe Kaya";     // setter
console.log(kisi._isim);        // "Ayşe"

💡 İpucu: Property descriptor'lar günlük kodda nadiren kullanılır, ama framework ve kütüphane kodlarında yaygındır. Vue 2'nin reactivity sistemi Object.defineProperty üzerine kuruludur. Bu kavramı bilmek, "sihir gibi" görünen framework davranışlarını anlamana yardımcı olur.


Yaygın Hatalar

1. Referans Karışıklığı

// ❌ Nesneler referans tiplidir — = ile kopyalanmaz!
const a = { x: 1, y: 2 };
const b = a;
b.x = 99;
console.log(a.x); // 99 — a da değişti!

// ✅ Kopya oluştur
const c = { ...a };
c.x = 50;
console.log(a.x); // 99 — değişmedi

2. Nested Object'te Spread Tuzağı

// ❌ Spread yüzeysel kopya — iç nesneler referans kalır
const config = { db: { host: "localhost", port: 5432 } };
const kopya = { ...config };
kopya.db.port = 3306;
console.log(config.db.port); // 3306 — ORİJİNAL DEĞİŞTİ!

// ✅ Derin kopya
const guvenliKopya = structuredClone(config);
guvenliKopya.db.port = 3306;
console.log(config.db.port); // 5432 — güvende

3. Optional Chaining Aşırı Kullanımı

// ❌ Her yere ?. koymak — hata maskeler
const isim = user?.profile?.name?.first?.toUpperCase();
// Sorun: user null ise sessizce undefined döner
// Belki hata fırlatılmalıydı — bug gizleniyor!

// ✅ Beklediğin yapıyı doğrula, gerekli yerde kullan
if (!user) throw new Error("Kullanıcı bulunamadı");
const isim = user.profile?.name ?? "İsimsiz";

Özet

  • 🔹 Nesneler anahtar-değer çiftleriyle veri saklar. Property'lere dot (nesne.prop) veya bracket (nesne["prop"]) notation ile erişilir

  • 🔹 Destructuring (const { isim, yas } = nesne) nesne özelliklerini değişkenlere atar; yeniden isimlendirme ve varsayılan değer destekler

  • 🔹 Spread ({ ...nesne }) ile yüzeysel kopya oluşturulur ve nesneler birleştirilir; rest ({ sifre, ...kalanı }) ile belirli property'ler ayrılır

  • 🔹 Object.keys/values/entries nesnenin anahtarlarını, değerlerini veya çiftlerini dizi olarak döndürür; Object.fromEntries tersi işlemi yapar

  • 🔹 Object.freeze nesneyi tamamen dondurur (ama yüzeysel), Object.seal güncellemeye izin verir ama ekleme/silmeyi engeller

  • 🔹 Nesne iterasyonunda for...in yerine Object.entries + for...of tercih edin — prototype sorunlarından kaçınır, daha güvenli ve okunabilirdir