Bu ders için video bulunmamaktadır.

Bu derse başlamak veya ilerlemenizi kaydetmek için lütfen giriş yapın veya kayıt olun.

Ders İçeriği

Giriş: Nesne Yönelimli Programlama

Nesne Yönelimli Programlama (Object-Oriented Programming, OOP), yazılım geliştirmede yaygın olarak kullanılan bir programlama paradigmasıdır. OOP, verileri ve davranışları bir araya getirerek "nesneler" oluşturmayı ve bu nesneler arasındaki etkileşimleri düzenlemeyi amaçlar.

JavaScript, başlangıçta prototip tabanlı bir dil olarak tasarlanmıştı, ancak ES6 (ECMAScript 2015) ile birlikte, diğer OOP dillerindekine benzer sınıf sözdizimi eklendi. Bu, JavaScript'te nesne yönelimli programlamayı daha tanıdık ve erişilebilir hale getirdi.

Bu derste, JavaScript'te nesne yönelimli programlamanın temellerini, sınıfların nasıl tanımlandığını ve kullanıldığını, kalıtım, kapsülleme ve polimorfizm gibi OOP kavramlarını öğreneceğiz.

JavaScript'te OOP'nin Evrimi

JavaScript'te nesne yönelimli programlama yaklaşımı zaman içinde evrim geçirmiştir. İşte bu evrimin kısa bir tarihçesi:

1. Nesne Literalleri

JavaScript'te en temel nesne oluşturma yöntemi, nesne literalleridir. Bu, özellikleri ve metodları doğrudan tanımlayan basit bir sözdizimi sunar.

nesne_literali.js
// Nesne literali ile nesne oluşturma
const ogrenci = {
    // Özellikler (properties)
    ad: "Ahmet",
    soyad: "Yılmaz",
    yas: 20,
    dersler: ["Matematik", "Fizik", "Programlama"],
    
    // Metodlar (methods)
    tamAd: function() {
        return this.ad + " " + this.soyad;
    },
    
    derseKayitOl: function(ders) {
        this.dersler.push(ders);
        return `${this.tamAd()}, ${ders} dersine kaydoldu.`;
    }
};

// Nesneyi kullanma
console.log(ogrenci.tamAd()); // "Ahmet Yılmaz"
console.log(ogrenci.derseKayitOl("Kimya")); // "Ahmet Yılmaz, Kimya dersine kaydoldu."
console.log(ogrenci.dersler); // ["Matematik", "Fizik", "Programlama", "Kimya"]

2. Kurucu Fonksiyonlar

Benzer nesnelerin birden fazla örneğini oluşturmak için, JavaScript'te geleneksel olarak kurucu fonksiyonlar kullanılır. Bu fonksiyonlar, new anahtar kelimesi ile çağrıldığında yeni bir nesne örneği oluşturur.

kurucu_fonksiyon.js
// Kurucu fonksiyon
function Ogrenci(ad, soyad, yas) {
    // Özellikler
    this.ad = ad;
    this.soyad = soyad;
    this.yas = yas;
    this.dersler = [];
    
    // Metod (her nesne örneği için ayrı oluşturulur)
    this.tamAd = function() {
        return this.ad + " " + this.soyad;
    };
}

// Prototip üzerinde metod tanımlama (tüm örnekler için tek bir kopya)
Ogrenci.prototype.derseKayitOl = function(ders) {
    this.dersler.push(ders);
    return `${this.tamAd()}, ${ders} dersine kaydoldu.`;
};

// Yeni örnekler oluşturma
const ogrenci1 = new Ogrenci("Ahmet", "Yılmaz", 20);
const ogrenci2 = new Ogrenci("Ayşe", "Kaya", 22);

// Nesneleri kullanma
console.log(ogrenci1.tamAd()); // "Ahmet Yılmaz"
console.log(ogrenci2.tamAd()); // "Ayşe Kaya"

console.log(ogrenci1.derseKayitOl("Matematik")); // "Ahmet Yılmaz, Matematik dersine kaydoldu."
console.log(ogrenci2.derseKayitOl("Fizik")); // "Ayşe Kaya, Fizik dersine kaydoldu."

3. ES6 Sınıfları

ES6 ile birlikte, JavaScript'e sınıf sözdizimi eklendi. Bu, aslında kurucu fonksiyonlar ve prototip kalıtımı üzerine inşa edilmiş bir sözdizimsel şekerdir (syntactic sugar), ancak daha temiz ve anlaşılır bir kod yazmanıza olanak tanır.

es6_sinif.js
// ES6 Sınıf tanımı
class Ogrenci {
    // Kurucu metod
    constructor(ad, soyad, yas) {
        this.ad = ad;
        this.soyad = soyad;
        this.yas = yas;
        this.dersler = [];
    }
    
    // Sınıf metodları (prototipe eklenir)
    tamAd() {
        return this.ad + " " + this.soyad;
    }
    
    derseKayitOl(ders) {
        this.dersler.push(ders);
        return `${this.tamAd()}, ${ders} dersine kaydoldu.`;
    }
}

// Yeni örnekler oluşturma
const ogrenci1 = new Ogrenci("Ahmet", "Yılmaz", 20);
const ogrenci2 = new Ogrenci("Ayşe", "Kaya", 22);

// Nesneleri kullanma
console.log(ogrenci1.tamAd()); // "Ahmet Yılmaz"
console.log(ogrenci2.tamAd()); // "Ayşe Kaya"

console.log(ogrenci1.derseKayitOl("Matematik")); // "Ahmet Yılmaz, Matematik dersine kaydoldu."
console.log(ogrenci2.derseKayitOl("Fizik")); // "Ayşe Kaya, Fizik dersine kaydoldu."

Not: ES6 sınıfları, JavaScript'in prototip tabanlı kalıtım modelini değiştirmez, sadece daha temiz bir sözdizimi sunar. Arka planda, sınıflar hala kurucu fonksiyonlar ve prototip kalıtımı kullanır.

ES6 Sınıfları Detaylı İnceleme

ES6 sınıfları, JavaScript'te nesne yönelimli programlama için daha temiz ve anlaşılır bir sözdizimi sunar. Bu bölümde, ES6 sınıflarının temel özelliklerini ve kullanım şekillerini detaylı olarak inceleyeceğiz.

1. Sınıf Tanımlama ve Kurucu

Bir sınıf, class anahtar kelimesi ile tanımlanır ve genellikle bir kurucu metod (constructor) içerir. Kurucu, sınıfın yeni bir örneği oluşturulduğunda otomatik olarak çağrılır.

Temel Sınıf Yapısı

Araba

- marka: String

- model: String

- yil: Number

- renk: String

+ constructor(marka, model, yil, renk)

+ bilgileriGoster(): String

+ calistir(): String

araba_sinifi.js
class Araba {
    // Kurucu metod
    constructor(marka, model, yil, renk) {
        this.marka = marka;
        this.model = model;
        this.yil = yil;
        this.renk = renk;
        this.hiz = 0;
    }
    
    // Sınıf metodları
    bilgileriGoster() {
        return `${this.marka} ${this.model} (${this.yil}), ${this.renk}`;
    }
    
    calistir() {
        return `${this.marka} ${this.model} çalıştırıldı.`;
    }
    
    hizlan(artis) {
        this.hiz += artis;
        return `Hız ${this.hiz} km/s'ye yükseltildi.`;
    }
    
    yavasla(azalis) {
        this.hiz = Math.max(0, this.hiz - azalis);
        return `Hız ${this.hiz} km/s'ye düşürüldü.`;
    }
}

// Yeni bir Araba örneği oluşturma
const araba1 = new Araba("Toyota", "Corolla", 2020, "Beyaz");

// Metodları kullanma
console.log(araba1.bilgileriGoster()); // "Toyota Corolla (2020), Beyaz"
console.log(araba1.calistir()); // "Toyota Corolla çalıştırıldı."
console.log(araba1.hizlan(50)); // "Hız 50 km/s'ye yükseltildi."
console.log(araba1.yavasla(20)); // "Hız 30 km/s'ye düşürüldü."

2. Statik Metodlar ve Özellikler

Statik metodlar ve özellikler, sınıfın kendisine ait olup, sınıfın örneklerine (instance) ait değildir. Bunlara, sınıf adı üzerinden erişilir.

statik_metodlar.js
class MathUtils {
    // Statik özellik (ES2022+)
    static PI = 3.14159;
    
    // Statik metodlar
    static topla(a, b) {
        return a + b;
    }
    
    static cikar(a, b) {
        return a - b;
    }
    
    static carp(a, b) {
        return a * b;
    }
    
    static bol(a, b) {
        if (b === 0) throw new Error("Sıfıra bölme hatası!");
        return a / b;
    }
    
    static daireAlani(yaricap) {
        return this.PI * yaricap * yaricap;
    }
}

// Statik metodları kullanma (örnek oluşturmadan)
console.log(MathUtils.topla(5, 3)); // 8
console.log(MathUtils.cikar(10, 4)); // 6
console.log(MathUtils.carp(2, 3)); // 6
console.log(MathUtils.bol(10, 2)); // 5
console.log(MathUtils.daireAlani(5)); // 78.53975

// Statik özelliğe erişim
console.log(MathUtils.PI); // 3.14159

// Hata: Statik metodlara örnek üzerinden erişilemez
const math = new MathUtils();
// console.log(math.topla(5, 3)); // TypeError: math.topla is not a function

Not: Statik özellikler (static fields), ES2022'de standartlaştırıldı. Daha eski tarayıcılarda veya Node.js sürümlerinde, statik özellikleri sınıf tanımının dışında tanımlamanız gerekebilir: MathUtils.PI = 3.14159;

3. Getter ve Setter Metodları

Getter ve setter metodları, sınıf özelliklerine erişimi ve değişikliği kontrol etmenize olanak tanır. Bu, kapsülleme (encapsulation) ilkesini uygulamanın bir yoludur.

getter_setter.js
class Kisi {
    constructor(ad, soyad, yas) {
        this._ad = ad;
        this._soyad = soyad;
        this._yas = yas;
    }
    
    // Getter metodları
    get ad() {
        return this._ad;
    }
    
    get soyad() {
        return this._soyad;
    }
    
    get tamAd() {
        return `${this._ad} ${this._soyad}`;
    }
    
    get yas() {
        return this._yas;
    }
    
    // Setter metodları
    set ad(yeniAd) {
        if (yeniAd.length < 2) {
            throw new Error("Ad en az 2 karakter olmalıdır.");
        }
        this._ad = yeniAd;
    }
    
    set soyad(yeniSoyad) {
        if (yeniSoyad.length < 2) {
            throw new Error("Soyad en az 2 karakter olmalıdır.");
        }
        this._soyad = yeniSoyad;
    }
    
    set yas(yeniYas) {
        if (yeniYas < 0 || yeniYas > 120) {
            throw new Error("Geçersiz yaş değeri.");
        }
        this._yas = yeniYas;
    }
}

// Yeni bir Kisi örneği oluşturma
const kisi = new Kisi("Ahmet", "Yılmaz", 30);

// Getter metodlarını kullanma
console.log(kisi.ad); // "Ahmet"
console.log(kisi.soyad); // "Yılmaz"
console.log(kisi.tamAd); // "Ahmet Yılmaz"
console.log(kisi.yas); // 30

// Setter metodlarını kullanma
kisi.ad = "Mehmet";
kisi.yas = 35;
console.log(kisi.tamAd); // "Mehmet Yılmaz"
console.log(kisi.yas); // 35

// Hata durumu
try {
    kisi.yas = 150; // Hata fırlatır
} catch (error) {
    console.error(error.message); // "Geçersiz yaş değeri."
}

İpucu: JavaScript'te, özel (private) özellikleri belirtmek için genellikle özellik adının başına alt çizgi (_) eklenir. Bu sadece bir konvansiyondur ve gerçek bir erişim kısıtlaması sağlamaz. ES2022'den itibaren, gerçek özel alanlar için # öneki kullanılabilir.

4. Özel Alanlar (Private Fields)

ES2022 ile birlikte, JavaScript'e gerçek özel alanlar eklendi. Özel alanlar, # öneki ile tanımlanır ve sınıf dışından erişilemez.

ozel_alanlar.js
class BankaHesabi {
    // Özel alanlar
    #hesapNo;
    #bakiye;
    #pin;
    
    constructor(hesapNo, baslangicBakiye, pin) {
        this.#hesapNo = hesapNo;
        this.#bakiye = baslangicBakiye;
        this.#pin = pin;
    }
    
    // Hesap bilgilerini gösterme
    hesapBilgileri() {
        return {
            hesapNo: this.#hesapNo,
            bakiye: this.#bakiye
        };
    }
    
    // Para yatırma
    paraYatir(miktar) {
        if (miktar <= 0) {
            throw new Error("Geçersiz miktar.");
        }
        this.#bakiye += miktar;
        return `${miktar} TL yatırıldı. Yeni bakiye: ${this.#bakiye} TL`;
    }
    
    // Para çekme (PIN doğrulaması ile)
    paraCek(miktar, pin) {
        if (pin !== this.#pin) {
            throw new Error("Geçersiz PIN.");
        }
        
        if (miktar <= 0) {
            throw new Error("Geçersiz miktar.");
        }
        
        if (miktar > this.#bakiye) {
            throw new Error("Yetersiz bakiye.");
        }
        
        this.#bakiye -= miktar;
        return `${miktar} TL çekildi. Yeni bakiye: ${this.#bakiye} TL`;
    }
    
    // PIN değiştirme
    pinDegistir(eskiPin, yeniPin) {
        if (eskiPin !== this.#pin) {
            throw new Error("Geçersiz PIN.");
        }
        
        if (yeniPin.length !== 4 || isNaN(yeniPin)) {
            throw new Error("PIN 4 haneli bir sayı olmalıdır.");
        }
        
        this.#pin = yeniPin;
        return "PIN başarıyla değiştirildi.";
    }
}

// Yeni bir BankaHesabi örneği oluşturma
const hesap = new BankaHesabi("123456789", 1000, "1234");

// Metodları kullanma
console.log(hesap.hesapBilgileri()); // { hesapNo: "123456789", bakiye: 1000 }
console.log(hesap.paraYatir(500)); // "500 TL yatırıldı. Yeni bakiye: 1500 TL"
console.log(hesap.paraCek(200, "1234")); // "200 TL çekildi. Yeni bakiye: 1300 TL"

// Hata durumları
try {
    console.log(hesap.paraCek(2000, "1234")); // Yetersiz bakiye
} catch (error) {
    console.error(error.message); // "Yetersiz bakiye."
}

try {
    console.log(hesap.paraCek(100, "4321")); // Yanlış PIN
} catch (error) {
    console.error(error.message); // "Geçersiz PIN."
}

// Özel alanlara doğrudan erişim hatası
try {
    console.log(hesap.#bakiye); // SyntaxError
} catch (error) {
    console.error("Özel alanlara dışarıdan erişilemez.");
}

Uyarı: Özel alanlar (# önekli) nispeten yeni bir özelliktir ve eski tarayıcılarda desteklenmeyebilir. Geniş tarayıcı desteği için, Babel gibi bir transpiler kullanabilir veya geleneksel alt çizgi (_) konvansiyonunu tercih edebilirsiniz.

Kalıtım (Inheritance)

Kalıtım, nesne yönelimli programlamanın temel kavramlarından biridir. Bir sınıfın, başka bir sınıfın özelliklerini ve metodlarını miras almasına olanak tanır. JavaScript'te kalıtım, extends anahtar kelimesi ile sağlanır.

Kalıtım Örneği

Arac

- marka: String

- model: String

- yil: Number

+ constructor(marka, model, yil)

+ bilgileriGoster(): String

extends
Otomobil

- kapiSayisi: Number

- yakitTuru: String

+ constructor(marka, model, yil, kapiSayisi, yakitTuru)

+ bilgileriGoster(): String (override)

kalitim.js
// Temel (üst) sınıf
class Arac {
    constructor(marka, model, yil) {
        this.marka = marka;
        this.model = model;
        this.yil = yil;
    }
    
    bilgileriGoster() {
        return `${this.marka} ${this.model} (${this.yil})`;
    }
    
    calistir() {
        return `${this.marka} ${this.model} çalıştırıldı.`;
    }
}

// Alt sınıf (Arac sınıfından miras alır)
class Otomobil extends Arac {
    constructor(marka, model, yil, kapiSayisi, yakitTuru) {
        // Üst sınıfın constructor'ını çağırma
        super(marka, model, yil);
        
        // Alt sınıfa özgü özellikler
        this.kapiSayisi = kapiSayisi;
        this.yakitTuru = yakitTuru;
    }
    
    // Üst sınıfın metodunu geçersiz kılma (override)
    bilgileriGoster() {
        // Üst sınıfın metodunu çağırma
        const temelBilgi = super.bilgileriGoster();
        return `${temelBilgi}, ${this.kapiSayisi} kapılı, ${this.yakitTuru}`;
    }
    
    // Alt sınıfa özgü metod
    bagajAc() {
        return `${this.marka} ${this.model} bagajı açıldı.`;
    }
}

// Başka bir alt sınıf
class Motosiklet extends Arac {
    constructor(marka, model, yil, motorHacmi) {
        super(marka, model, yil);
        this.motorHacmi = motorHacmi;
    }
    
    bilgileriGoster() {
        const temelBilgi = super.bilgileriGoster();
        return `${temelBilgi}, ${this.motorHacmi}cc`;
    }
    
    // Alt sınıfa özgü metod
    stunYap() {
        return `${this.marka} ${this.model} ile stunt yapılıyor!`;
    }
}

// Örnekler oluşturma
const arac = new Arac("Genel", "Taşıt", 2020);
const otomobil = new Otomobil("Toyota", "Corolla", 2020, 4, "Benzin");
const motosiklet = new Motosiklet("Honda", "CBR", 2021, 1000);

// Metodları kullanma
console.log(arac.bilgileriGoster()); // "Genel Taşıt (2020)"
console.log(otomobil.bilgileriGoster()); // "Toyota Corolla (2020), 4 kapılı, Benzin"
console.log(motosiklet.bilgileriGoster()); // "Honda CBR (2021), 1000cc"

console.log(arac.calistir()); // "Genel Taşıt çalıştırıldı."
console.log(otomobil.calistir()); // "Toyota Corolla çalıştırıldı."
console.log(motosiklet.calistir()); // "Honda CBR çalıştırıldı."

console.log(otomobil.bagajAc()); // "Toyota Corolla bagajı açıldı."
console.log(motosiklet.stunYap()); // "Honda CBR ile stunt yapılıyor!"

// instanceof operatörü ile tip kontrolü
console.log(otomobil instanceof Otomobil); // true
console.log(otomobil instanceof Arac); // true
console.log(otomobil instanceof Motosiklet); // false

Çoklu Kalıtım ve Mixin'ler

JavaScript, doğrudan çoklu kalıtımı desteklemez (bir sınıf yalnızca bir sınıftan miras alabilir). Ancak, mixin'ler kullanarak benzer bir işlevsellik elde edebilirsiniz. Mixin'ler, sınıflara ek özellikler ve metodlar eklemek için kullanılan nesnelerdir.

mixin.js
// Mixin'ler
const canSwimMixin = {
    swim() {
        return `${this.constructor.name} yüzüyor.`;
    }
};

const canFlyMixin = {
    fly() {
        return `${this.constructor.name} uçuyor.`;
    }
};

// Temel sınıf
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    eat() {
        return `${this.name} yemek yiyor.`;
    }
}

// Alt sınıflar
class Bird extends Animal {
    constructor(name) {
        super(name);
    }
}

class Fish extends Animal {
    constructor(name) {
        super(name);
    }
}

class Duck extends Animal {
    constructor(name) {
        super(name);
    }
}

// Mixin'leri uygulama
Object.assign(Bird.prototype, canFlyMixin);
Object.assign(Fish.prototype, canSwimMixin);
Object.assign(Duck.prototype, canFlyMixin, canSwimMixin);

// Örnekler oluşturma
const bird = new Bird("Serçe");
const fish = new Fish("Balık");
const duck = new Duck("Ördek");

// Metodları kullanma
console.log(bird.eat()); // "Serçe yemek yiyor."
console.log(bird.fly()); // "Bird uçuyor."

console.log(fish.eat()); // "Balık yemek yiyor."
console.log(fish.swim()); // "Fish yüzüyor."

console.log(duck.eat()); // "Ördek yemek yiyor."
console.log(duck.fly()); // "Duck uçuyor."
console.log(duck.swim()); // "Duck yüzüyor."

Polimorfizm (Polymorphism)

Polimorfizm, farklı sınıfların aynı metod adlarını kullanarak farklı davranışlar sergilemesine olanak tanır. JavaScript'te polimorfizm, genellikle metod geçersiz kılma (method overriding) ile uygulanır.

polimorfizm.js
// Temel sınıf
class Sekil {
    constructor() {
        if (this.constructor === Sekil) {
            throw new Error("Sekil sınıfı soyuttur, doğrudan örneği oluşturulamaz.");
        }
    }
    
    alan() {
        throw new Error("Alt sınıflar alan() metodunu uygulamalıdır.");
    }
    
    cevre() {
        throw new Error("Alt sınıflar cevre() metodunu uygulamalıdır.");
    }
    
    bilgileriGoster() {
        return `Alan: ${this.alan()}, Çevre: ${this.cevre()}`;
    }
}

// Alt sınıflar
class Dikdortgen extends Sekil {
    constructor(genislik, yukseklik) {
        super();
        this.genislik = genislik;
        this.yukseklik = yukseklik;
    }
    
    alan() {
        return this.genislik * this.yukseklik;
    }
    
    cevre() {
        return 2 * (this.genislik + this.yukseklik);
    }
    
    bilgileriGoster() {
        return `Dikdörtgen - ${super.bilgileriGoster()}, Genişlik: ${this.genislik}, Yükseklik: ${this.yukseklik}`;
    }
}

class Daire extends Sekil {
    constructor(yaricap) {
        super();
        this.yaricap = yaricap;
    }
    
    alan() {
        return Math.PI * this.yaricap * this.yaricap;
    }
    
    cevre() {
        return 2 * Math.PI * this.yaricap;
    }
    
    bilgileriGoster() {
        return `Daire - ${super.bilgileriGoster()}, Yarıçap: ${this.yaricap}`;
    }
}

class Ucgen extends Sekil {
    constructor(a, b, c) {
        super();
        this.a = a;
        this.b = b;
        this.c = c;
    }
    
    alan() {
        // Heron formülü
        const s = (this.a + this.b + this.c) / 2;
        return Math.sqrt(s * (s - this.a) * (s - this.b) * (s - this.c));
    }
    
    cevre() {
        return this.a + this.b + this.c;
    }
    
    bilgileriGoster() {
        return `Üçgen - ${super.bilgileriGoster()}, Kenarlar: ${this.a}, ${this.b}, ${this.c}`;
    }
}

// Polimorfik fonksiyon
function sekilBilgileriniGoster(sekil) {
    if (!(sekil instanceof Sekil)) {
        throw new Error("Parametre bir Sekil örneği olmalıdır.");
    }
    return sekil.bilgileriGoster();
}

// Örnekler oluşturma
const dikdortgen = new Dikdortgen(5, 3);
const daire = new Daire(4);
const ucgen = new Ucgen(3, 4, 5);

// Polimorfik fonksiyonu kullanma
console.log(sekilBilgileriniGoster(dikdortgen));
// "Dikdörtgen - Alan: 15, Çevre: 16, Genişlik: 5, Yükseklik: 3"

console.log(sekilBilgileriniGoster(daire));
// "Daire - Alan: 50.26548245743669, Çevre: 25.132741228718345, Yarıçap: 4"

console.log(sekilBilgileriniGoster(ucgen));
// "Üçgen - Alan: 6, Çevre: 12, Kenarlar: 3, 4, 5"

// Hata durumu
try {
    const sekil = new Sekil(); // Hata fırlatır
} catch (error) {
    console.error(error.message); // "Sekil sınıfı soyuttur, doğrudan örneği oluşturulamaz."
}

Not: JavaScript'te, Java veya C# gibi dillerdeki gibi gerçek soyut sınıflar veya arayüzler (interfaces) yoktur. Ancak, yukarıdaki örnekte gösterildiği gibi, hata fırlatma ve metod geçersiz kılma ile benzer bir davranış elde edebilirsiniz.

Kapsülleme (Encapsulation)

Kapsülleme, bir sınıfın iç durumunu (özelliklerini) gizleyerek, yalnızca kontrollü bir arayüz (metodlar) aracılığıyla erişime izin verme ilkesidir. JavaScript'te kapsülleme, özel alanlar, closure'lar veya konvansiyonlar (alt çizgi öneki) kullanılarak uygulanabilir.

1. Özel Alanlar ile Kapsülleme

kapsulleme_ozel_alanlar.js
class Kullanici {
    // Özel alanlar
    #ad;
    #soyad;
    #email;
    #sifre;
    
    constructor(ad, soyad, email, sifre) {
        this.#ad = ad;
        this.#soyad = soyad;
        this.#email = this.#emailDogrula(email) ? email : throw new Error("Geçersiz e-posta adresi.");
        this.#sifre = this.#sifreDogrula(sifre) ? sifre : throw new Error("Geçersiz şifre.");
    }
    
    // Özel metodlar
    #emailDogrula(email) {
        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return regex.test(email);
    }
    
    #sifreDogrula(sifre) {
        return sifre.length >= 8;
    }
    
    // Getter metodları
    get ad() {
        return this.#ad;
    }
    
    get soyad() {
        return this.#soyad;
    }
    
    get tamAd() {
        return `${this.#ad} ${this.#soyad}`;
    }
    
    get email() {
        return this.#email;
    }
    
    // Setter metodları
    set ad(yeniAd) {
        if (yeniAd.length < 2) {
            throw new Error("Ad en az 2 karakter olmalıdır.");
        }
        this.#ad = yeniAd;
    }
    
    set soyad(yeniSoyad) {
        if (yeniSoyad.length < 2) {
            throw new Error("Soyad en az 2 karakter olmalıdır.");
        }
        this.#soyad = yeniSoyad;
    }
    
    set email(yeniEmail) {
        if (!this.#emailDogrula(yeniEmail)) {
            throw new Error("Geçersiz e-posta adresi.");
        }
        this.#email = yeniEmail;
    }
    
    // Şifre değiştirme metodu
    sifreDegistir(eskiSifre, yeniSifre) {
        if (eskiSifre !== this.#sifre) {
            throw new Error("Mevcut şifre yanlış.");
        }
        
        if (!this.#sifreDogrula(yeniSifre)) {
            throw new Error("Yeni şifre en az 8 karakter olmalıdır.");
        }
        
        this.#sifre = yeniSifre;
        return "Şifre başarıyla değiştirildi.";
    }
    
    // Kullanıcı bilgilerini gösterme
    bilgileriGoster() {
        return {
            ad: this.#ad,
            soyad: this.#soyad,
            email: this.#email
        };
    }
    
    // Giriş kontrolü
    girisYap(email, sifre) {
        if (email === this.#email && sifre === this.#sifre) {
            return "Giriş başarılı.";
        } else {
            return "E-posta veya şifre yanlış.";
        }
    }
}

// Kullanıcı örneği oluşturma
const kullanici = new Kullanici("Ahmet", "Yılmaz", "ahmet@example.com", "12345678");

// Getter metodlarını kullanma
console.log(kullanici.tamAd); // "Ahmet Yılmaz"
console.log(kullanici.email); // "ahmet@example.com"

// Setter metodlarını kullanma
kullanici.ad = "Mehmet";
console.log(kullanici.tamAd); // "Mehmet Yılmaz"

// Bilgileri gösterme
console.log(kullanici.bilgileriGoster());
// { ad: "Mehmet", soyad: "Yılmaz", email: "ahmet@example.com" }

// Giriş kontrolü
console.log(kullanici.girisYap("ahmet@example.com", "12345678")); // "Giriş başarılı."
console.log(kullanici.girisYap("ahmet@example.com", "yanlissifre")); // "E-posta veya şifre yanlış."

// Şifre değiştirme
console.log(kullanici.sifreDegistir("12345678", "yenisifre123")); // "Şifre başarıyla değiştirildi."

// Hata durumları
try {
    kullanici.email = "gecersiz-email"; // Hata fırlatır
} catch (error) {
    console.error(error.message); // "Geçersiz e-posta adresi."
}

try {
    console.log(kullanici.#sifre); // SyntaxError
} catch (error) {
    console.error("Özel alanlara dışarıdan erişilemez.");
}

2. Closure ile Kapsülleme

Özel alanlar henüz desteklenmeyen ortamlarda, closure'lar kullanarak kapsülleme sağlayabilirsiniz.

kapsulleme_closure.js
function createKullanici(ad, soyad, email, sifre) {
    // Özel değişkenler (closure içinde)
    let _ad = ad;
    let _soyad = soyad;
    let _email = email;
    let _sifre = sifre;
    
    // Özel fonksiyonlar
    function emailDogrula(email) {
        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return regex.test(email);
    }
    
    function sifreDogrula(sifre) {
        return sifre.length >= 8;
    }
    
    // Dışa açılan API (public interface)
    return {
        // Getter metodları
        getAd: function() {
            return _ad;
        },
        
        getSoyad: function() {
            return _soyad;
        },
        
        getTamAd: function() {
            return `${_ad} ${_soyad}`;
        },
        
        getEmail: function() {
            return _email;
        },
        
        // Setter metodları
        setAd: function(yeniAd) {
            if (yeniAd.length < 2) {
                throw new Error("Ad en az 2 karakter olmalıdır.");
            }
            _ad = yeniAd;
        },
        
        setSoyad: function(yeniSoyad) {
            if (yeniSoyad.length < 2) {
                throw new Error("Soyad en az 2 karakter olmalıdır.");
            }
            _soyad = yeniSoyad;
        },
        
        setEmail: function(yeniEmail) {
            if (!emailDogrula(yeniEmail)) {
                throw new Error("Geçersiz e-posta adresi.");
            }
            _email = yeniEmail;
        },
        
        // Şifre değiştirme metodu
        sifreDegistir: function(eskiSifre, yeniSifre) {
            if (eskiSifre !== _sifre) {
                throw new Error("Mevcut şifre yanlış.");
            }
            
            if (!sifreDogrula(yeniSifre)) {
                throw new Error("Yeni şifre en az 8 karakter olmalıdır.");
            }
            
            _sifre = yeniSifre;
            return "Şifre başarıyla değiştirildi.";
        },
        
        // Kullanıcı bilgilerini gösterme
        bilgileriGoster: function() {
            return {
                ad: _ad,
                soyad: _soyad,
                email: _email
            };
        },
        
        // Giriş kontrolü
        girisYap: function(email, sifre) {
            if (email === _email && sifre === _sifre) {
                return "Giriş başarılı.";
            } else {
                return "E-posta veya şifre yanlış.";
            }
        }
    };
}

// Kullanıcı örneği oluşturma
const kullanici = createKullanici("Ahmet", "Yılmaz", "ahmet@example.com", "12345678");

// Metodları kullanma
console.log(kullanici.getTamAd()); // "Ahmet Yılmaz"
console.log(kullanici.getEmail()); // "ahmet@example.com"

kullanici.setAd("Mehmet");
console.log(kullanici.getTamAd()); // "Mehmet Yılmaz"

console.log(kullanici.bilgileriGoster());
// { ad: "Mehmet", soyad: "Yılmaz", email: "ahmet@example.com" }

console.log(kullanici.girisYap("ahmet@example.com", "12345678")); // "Giriş başarılı."

Alıştırmalar

  1. Bir Hesap sınıfı oluşturun. Bu sınıf, hesap numarası, sahibi ve bakiye gibi özelliklere sahip olmalıdır. Para yatırma, para çekme ve bakiye sorgulama gibi metodlar ekleyin. Özel alanlar kullanarak kapsülleme uygulayın.
  2. Hesap sınıfından miras alan VadesizHesap ve VadeliHesap alt sınıfları oluşturun. VadeliHesap sınıfı, vade süresi ve faiz oranı gibi ek özelliklere sahip olmalıdır. Her iki sınıf için de para çekme işlemini farklı şekilde uygulayın (polimorfizm).
  3. Bir Banka sınıfı oluşturun. Bu sınıf, hesapları yönetmeli ve hesap açma, hesap kapatma, para transferi gibi işlemleri gerçekleştirmelidir.
  4. Bir Sekil sınıfı ve bu sınıftan miras alan DikdortgenDaire ve Ucgen sınıfları oluşturun. Her sınıf, alan ve çevre hesaplama metodlarını uygulamalıdır. Statik bir metod ekleyerek, iki şeklin alanını karşılaştıran bir fonksiyon yazın.
  5. Bir Canli sınıfı ve bu sınıftan miras alan Hayvan ve Bitki sınıfları oluşturun. Hayvan sınıfından miras alan MemeliKus ve Balik sınıfları oluşturun. Her sınıf için uygun özellikler ve metodlar ekleyin. Mixin'ler kullanarak CanYuzen ve CanUcan gibi yetenekler ekleyin.

Bu derste, JavaScript'te nesne yönelimli programlamanın temellerini ve ES6 sınıflarını inceledik:

  • JavaScript'te OOP'nin Evrimi: Nesne literalleri, kurucu fonksiyonlar ve ES6 sınıfları.
  • ES6 Sınıfları: class anahtar kelimesi, constructor metodu, sınıf metodları, statik metodlar ve özellikler, getter ve setter metodları, özel alanlar.
  • Kalıtım (Inheritance): extends anahtar kelimesi, super anahtar kelimesi, metod geçersiz kılma (method overriding), mixin'ler.
  • Polimorfizm (Polymorphism): Farklı sınıfların aynı metod adlarını kullanarak farklı davranışlar sergilemesi.
  • Kapsülleme (Encapsulation): Özel alanlar, closure'lar ve konvansiyonlar ile veri gizleme ve kontrollü erişim.

ES6 sınıfları, JavaScript'te nesne yönelimli programlama için daha temiz ve anlaşılır bir sözdizimi sunar. Ancak, arka planda hala JavaScript'in prototip tabanlı kalıtım modeli kullanılır.

Nesne yönelimli programlama, kodunuzu daha modüler, yeniden kullanılabilir ve bakımı kolay hale getirmenize yardımcı olur. Özellikle büyük ve karmaşık uygulamalarda, OOP prensiplerini uygulamak, kodunuzu daha iyi organize etmenize ve yönetmenize olanak tanır.

Bir sonraki derste, modern JavaScript'in diğer önemli özelliklerinden biri olan hata yönetimi ve asenkron programlama konularını daha derinlemesine inceleyeceğiz.