Koşullar ve Döngüler
Giriş — Programları Akıllı Kılmak
Şu ana kadar öğrendiğimiz her şey — değişkenler, tipler, operatörler — birer yapı taşıydı. Ama bir program sadece yukarıdan aşağıya sırayla çalışan komutlardan ibaret değildir. Programları güçlü kılan şey, karar verebilme ve tekrarlayabilme yetenekleridir.
Gerçek hayattan düşünün: Sabah kalktığınızda "Hava yağmurlu ise şemsiye al, değilse alma" dersiniz — bu bir koşuldur. "Tabaktaki yemek bitene kadar ye" dersiniz — bu bir döngüdür. Programlar da aynı mantıkla çalışır: koşullara göre farklı yollara sapabilir, aynı işlemi tekrar tekrar yapabilir.
Analoji: Bir program, bir trafik kavşağı gibidir. Düz giden yol (sıralı çalışma) tek başına yeterli değildir. Kırmızı ışıkta dur, yeşilde geç (koşul). Kavşağı 10 kez dönmek yerine bir döner kavşakta istediğin çıkışı al (döngü). Koşullar ve döngüler, programınızı düz bir yoldan zeki bir navigasyon sistemine dönüştürür.
if / else — Karar Mekanizması
if ifadesi, JavaScript'in en temel karar yapısıdır. Bir koşul true ise bir bloğu çalıştırır, false ise atlar veya alternatif bir bloğu çalıştırır.
Temel Sözdizimi
// En basit form
let sicaklik = 35;
if (sicaklik > 30) {
console.log("Hava çok sıcak! Bol su için.");
}
// if...else
let yas = 16;
if (yas >= 18) {
console.log("Ehliyet alabilirsiniz.");
} else {
console.log("Ehliyet için henüz yaşınız yeterli değil.");
console.log(`${18 - yas} yıl daha beklemeniz gerekiyor.`);
}else if — Çoklu Koşullar
let puan = 73;
let harf;
if (puan >= 90) {
harf = "A";
console.log("Mükemmel!");
} else if (puan >= 80) {
harf = "B";
console.log("Çok iyi!");
} else if (puan >= 70) {
harf = "C";
console.log("İyi.");
} else if (puan >= 60) {
harf = "D";
console.log("Geçer.");
} else {
harf = "F";
console.log("Maalesef kaldınız.");
}
console.log(`Harf notunuz: ${harf}`); // CÖnemli: else if zincirleri yukarıdan aşağıya değerlendirilir ve ilk eşleşen blok çalıştırılır, geri kalanlar atlanır. Bu yüzden koşulların sırası önemlidir:
// ❌ YANLIŞ sıralama — 90+ puanlar da ilk koşula girer
let puan2 = 95;
if (puan2 >= 60) {
console.log("Geçti"); // 95 >= 60 true, buraya girer
} else if (puan2 >= 90) {
console.log("Mükemmel"); // Buraya hiç ulaşamaz!
}
// ✅ DOĞRU — en spesifik koşul en üstte
if (puan2 >= 90) {
console.log("Mükemmel"); // 95 >= 90 true, buraya girer
} else if (puan2 >= 60) {
console.log("Geçti");
}Koşulda Truthy/Falsy Kullanımı
Bir önceki derste öğrendiğimiz truthy/falsy kavramı burada devreye girer:
let kullaniciAdi = "Ali";
// Truthy kontrolü — değerin var olup olmadığını kontrol eder
if (kullaniciAdi) {
console.log(`Hoş geldin, ${kullaniciAdi}!`);
} else {
console.log("Lütfen giriş yapın.");
}
// Yaygın truthy/falsy kontrolleri
let dizi = [];
if (dizi.length) { // 0 falsy, pozitif sayılar truthy
console.log("Dizi dolu");
} else {
console.log("Dizi boş");
}Tek Satırlık if (Süslü Parantez Olmadan)
// Tek satırlık form — süslü parantez olmadan
if (yas >= 18) console.log("Yetişkin");
// UYARI: Bu form sadece tek bir ifade için geçerlidir
// Birden fazla satır varsa MUTLAKA süslü parantez kullanın
// ❌ TEHLİKELİ — ikinci satır her zaman çalışır
if (false)
console.log("Bu çalışmaz");
console.log("Bu HER ZAMAN çalışır!"); // if'in dışında!
// ✅ GÜVENLİ — her zaman süslü parantez kullanın
if (false) {
console.log("Bu çalışmaz");
console.log("Bu da çalışmaz");
}⚠️ Dikkat: Süslü parantez olmadan if kullanmak yaygın bir hata kaynağıdır. Profesyonel projelerde ESLint'in curly kuralı bunu zorunlu kılar. Her zaman süslü parantez kullanın.
switch — Çoklu Değer Eşleştirme
switch, bir değişkenin birden fazla değere karşı kontrol edilmesi gerektiğinde kullanılır. Uzun else if zincirleri yerine daha okunabilir bir yapı sunar.
let gun = "Çarşamba";
switch (gun) {
case "Pazartesi":
console.log("Hafta başladı, kahve şart! ☕");
break;
case "Salı":
case "Çarşamba":
case "Perşembe":
console.log("Hafta ortası, devam! 💪");
break;
case "Cuma":
console.log("Cuma! Hafta sonu yaklaşıyor! 🎉");
break;
case "Cumartesi":
case "Pazar":
console.log("Hafta sonu, keyif zamanı! 🏖️");
break;
default:
console.log("Geçersiz gün adı.");
}break'in Önemi — Fall-through
break yazmazsanız, eşleşen case'den sonra gelen tüm case'ler de çalışır. Buna "fall-through" denir:
// ❌ break unutulursa — fall-through
let meyve = "elma";
switch (meyve) {
case "elma":
console.log("Elma seçildi"); // ✅ Çalışır
case "armut":
console.log("Armut seçildi"); // ❌ Bu da çalışır! break yok
case "portakal":
console.log("Portakal seçildi"); // ❌ Bu da çalışır!
break;
default:
console.log("Bilinmeyen meyve");
}
// Çıktı:
// Elma seçildi
// Armut seçildi
// Portakal seçildi// ✅ break ile doğru kullanım
switch (meyve) {
case "elma":
console.log("Elma seçildi");
break; // Burada durur, aşağı inmez
case "armut":
console.log("Armut seçildi");
break;
default:
console.log("Bilinmeyen meyve");
}switch vs if/else — Ne Zaman Hangisi?
// switch — belirli değerlere karşı kontrol (eşitlik)
switch (status) {
case 200: /* ... */ break;
case 404: /* ... */ break;
case 500: /* ... */ break;
}
// if/else — aralık, karmaşık koşullar
if (puan >= 90) { /* ... */ }
else if (puan >= 60) { /* ... */ }
else { /* ... */ }Kural: Belirli değerlere karşı eşitlik kontrolü yapıyorsanız switch, aralık veya karmaşık koşullar varsa if/else kullanın. Ayrıca switch her zaman === (sıkı eşitlik) kullanır.
💡 İpucu: Modern JavaScript'te object lookup, bazı switch/if-else yapılarının yerine daha temiz bir alternatif sunabilir:
// switch yerine object lookup
const gunMesajlari = {
Pazartesi: "Hafta başladı ☕",
Cuma: "Hafta sonu yaklaşıyor 🎉",
Cumartesi: "Hafta sonu 🏖️",
Pazar: "Hafta sonu 🏖️"
};
let mesaj = gunMesajlari[gun] ?? "Sıradan bir gün";
console.log(mesaj);for Döngüsü — Klasik Tekrarlama
for döngüsü, belirli sayıda tekrar yapılması gerektiğinde kullanılır. Üç bileşeni vardır: başlangıç, koşul, güncelleme.
// Temel for döngüsü
for (let i = 0; i < 5; i++) {
console.log(`Adım ${i + 1}`);
}
// Adım 1, Adım 2, Adım 3, Adım 4, Adım 5
// Geriye doğru sayma
for (let i = 10; i >= 0; i--) {
console.log(i);
}
// İkişer atlama
for (let i = 0; i <= 20; i += 2) {
console.log(i); // 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20
}Dizi ile for Döngüsü
const meyveler = ["elma", "armut", "portakal", "muz", "kivi"];
// Index ile erişim
for (let i = 0; i < meyveler.length; i++) {
console.log(`${i + 1}. ${meyveler[i]}`);
}
// Tersten iterasyon
for (let i = meyveler.length - 1; i >= 0; i--) {
console.log(meyveler[i]); // kivi, muz, portakal, armut, elma
}İç İçe (Nested) Döngüler
// Çarpım tablosu
for (let i = 1; i <= 5; i++) {
let satir = "";
for (let j = 1; j <= 5; j++) {
satir += `${(i * j).toString().padStart(4)}`;
}
console.log(satir);
}
// 1 2 3 4 5
// 2 4 6 8 10
// 3 6 9 12 15
// 4 8 12 16 20
// 5 10 15 20 25⚠️ Dikkat: İç içe döngüler, iterasyon sayısını çarparak artırır. Dıştaki döngü 1000, içteki de 1000 kez dönerse toplam 1.000.000 iterasyon olur. Büyük veri setlerinde performans sorunlarına yol açabilir. Mümkünse iç içe döngülerden kaçının veya alternatif algoritma kullanın.
while ve do...while
while — Koşul Doğru Olduğu Sürece
// Temel while
let sayac = 1;
while (sayac <= 5) {
console.log(`Sayaç: ${sayac}`);
sayac++;
}
// Gerçek dünya örneği — şifre doğrulama simülasyonu
let denemeSayisi = 0;
const maxDeneme = 3;
let girisBasarili = false;
while (denemeSayisi < maxDeneme && !girisBasarili) {
denemeSayisi++;
// Simüle: 3. denemede doğru şifreyi girer
let girilenSifre = denemeSayisi === 3 ? "dogru123" : "yanlis";
if (girilenSifre === "dogru123") {
girisBasarili = true;
console.log("✅ Giriş başarılı!");
} else {
console.log(`❌ Hatalı şifre. ${maxDeneme - denemeSayisi} hak kaldı.`);
}
}
if (!girisBasarili) {
console.log("🔒 Hesabınız kilitlendi.");
}do...while — En Az Bir Kez Çalış
while ile do...while arasındaki fark: do...while koşulu sonra kontrol eder, yani döngü gövdesi en az bir kez çalıştırılır:
// do...while — koşul yanlış olsa bile en az bir kez çalışır
let sayi = 10;
do {
console.log(`Sayı: ${sayi}`); // Bu satır çalışır
sayi++;
} while (sayi < 5); // Koşul false, ama gövde zaten bir kez çalıştı
// while ile aynı koşul — hiç çalışmaz
sayi = 10;
while (sayi < 5) {
console.log(`Sayı: ${sayi}`); // Bu satır hiç çalışmaz
sayi++;
}Ne zaman `do...while` kullanılır? Kullanıcıdan input alınması veya bir işlemin en az bir kez yapılıp sonra koşulun kontrol edilmesi gereken durumlarda:
// Menü sistemi simülasyonu
let secim;
do {
console.log("\n=== MENÜ ===");
console.log("1. Yeni Oyun");
console.log("2. Devam Et");
console.log("3. Ayarlar");
console.log("0. Çıkış");
secim = Math.floor(Math.random() * 4); // Simüle
console.log(`Seçim: ${secim}`);
switch (secim) {
case 1: console.log("Yeni oyun başlatılıyor..."); break;
case 2: console.log("Kayıt yükleniyor..."); break;
case 3: console.log("Ayarlar açılıyor..."); break;
case 0: console.log("Hoşça kal!"); break;
}
} while (secim !== 0);break ve continue — Döngü Kontrolü
break — Döngüyü Tamamen Sonlandır
// Aranan elemanı bul ve dur
const sayilar = [3, 7, 12, 5, 18, 2, 9];
let aranan = 18;
let bulunanIndex = -1;
for (let i = 0; i < sayilar.length; i++) {
if (sayilar[i] === aranan) {
bulunanIndex = i;
break; // Bulduk, döngüye devam etmeye gerek yok
}
}
console.log(bulunanIndex !== -1
? `${aranan} index ${bulunanIndex}'de bulundu`
: `${aranan} bulunamadı`
);continue — Bu Adımı Atla, Sonrakine Geç
// Sadece çift sayıları yazdır
for (let i = 1; i <= 10; i++) {
if (i % 2 !== 0) {
continue; // Tek sayıları atla
}
console.log(i); // 2, 4, 6, 8, 10
}
// Geçersiz verileri filtrele
const veriler = ["Ali", "", "Ayşe", null, "Mehmet", undefined, "Fatma"];
for (let i = 0; i < veriler.length; i++) {
if (!veriler[i]) {
continue; // Falsy değerleri atla
}
console.log(`İşleniyor: ${veriler[i]}`);
}Label ile İç İçe Döngülerden Çıkış
// İç içe döngüde dıştakini de sonlandırma
disDongu:
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
if (i === 2 && j === 3) {
console.log(`Çıkış noktası: i=${i}, j=${j}`);
break disDongu; // Dıştaki döngüyü de kırar
}
console.log(`i=${i}, j=${j}`);
}
}💡 İpucu: Label'lı break/continue nadiren kullanılır ve kodu karmaşıklaştırabilir. Çoğu durumda iç içe döngüyü bir fonksiyona çıkarıp return kullanmak daha temiz bir çözümdür.
for...of — Değerler Üzerinde İterasyon (ES6)
for...of, iterable (yinelenebilir) nesneler üzerinde doğrudan değerlere erişmenizi sağlar. Diziler, stringler, Map, Set gibi yapılarla çalışır:
// Dizi üzerinde for...of
const renkler = ["kırmızı", "mavi", "yeşil"];
for (const renk of renkler) {
console.log(renk);
}
// kırmızı, mavi, yeşil
// String üzerinde for...of — her karakter
const kelime = "Merhaba";
for (const harf of kelime) {
console.log(harf);
}
// M, e, r, h, a, b, a
// Index'e de ihtiyacınız varsa — entries() kullanın
for (const [index, renk] of renkler.entries()) {
console.log(`${index}: ${renk}`);
}
// 0: kırmızı, 1: mavi, 2: yeşilfor vs for...of — Ne Zaman Hangisi?
const meyveler = ["elma", "armut", "portakal"];
// ✅ for...of — basit iterasyon (çoğu durumda)
for (const meyve of meyveler) {
console.log(meyve);
}
// ✅ for — index'e ihtiyaç varsa veya geriye doğru gidecekseniz
for (let i = meyveler.length - 1; i >= 0; i--) {
console.log(meyveler[i]);
}
// ✅ for — karmaşık adım atlaması gerekiyorsa
for (let i = 0; i < meyveler.length; i += 2) {
console.log(meyveler[i]);
}for...in — Nesne Özellikleri Üzerinde İterasyon
for...in, bir nesnenin enumerable (sayılabilir) özelliklerinin anahtarları üzerinde döner:
const kullanici = {
isim: "Ali",
yas: 30,
sehir: "İstanbul",
meslek: "Geliştirici"
};
for (const anahtar in kullanici) {
console.log(`${anahtar}: ${kullanici[anahtar]}`);
}
// isim: Ali
// yas: 30
// sehir: İstanbul
// meslek: Geliştirici⚠️ Dikkat: for...in dizilerle kullanmayın! Dizilerde for...of veya dizi metotları kullanın. for...in dizilerde sorunlara yol açabilir:
const dizi = ["a", "b", "c"];
// ❌ for...in dizide — sorunlu
for (const index in dizi) {
console.log(typeof index); // "string"! — index string olarak gelir
console.log(index); // "0", "1", "2" — string!
}
// Daha kötüsü: prototype'tan gelen özellikler de dönebilir
Array.prototype.ozelMetot = function() {};
for (const key in dizi) {
console.log(key); // "0", "1", "2", "ozelMetot" — istenmeyen!
}
// ✅ Dizide for...of kullanın
for (const eleman of dizi) {
console.log(eleman); // "a", "b", "c" — temiz
}Özet kuralı: for...of → değerler (diziler, stringler), for...in → anahtarlar (nesneler).
Sonsuz Döngü — En Büyük Tuzak
Döngü koşulu hiçbir zaman false olmazsa, döngü sonsuza kadar devam eder ve programınız donar:
// ❌ SONSUZ DÖNGÜ — koşul hiç false olmaz
// while (true) {
// console.log("Sonsuza kadar..."); // Tarayıcı donacaktır!
// }
// ❌ SONSUZ DÖNGÜ — sayac hiç artmıyor
// let i = 0;
// while (i < 10) {
// console.log(i);
// // i++ unutulmuş!
// }
// ❌ SONSUZ DÖNGÜ — yanlış yönde sayma
// for (let i = 10; i >= 0; i++) { // i artıyor, hiç 0'ın altına düşmez
// console.log(i);
// }
// ✅ Güvenli "sonsuz" döngü — break ile kontrollü çıkış
let denemeler = 0;
while (true) {
denemeler++;
let rastgele = Math.random();
if (rastgele > 0.95) {
console.log(`${denemeler}. denemede 0.95'i geçtik!`);
break; // Kontrollü çıkış
}
// Güvenlik sınırı
if (denemeler > 10000) {
console.log("Çok fazla deneme, durduruluyor.");
break;
}
}Gerçek Dünya Örneği: Basit Hesap Makinesi
Tüm kontrol yapılarını birleştiren kapsamlı bir örnek:
// Hesap makinesi — koşullar, döngüler, switch, hata yönetimi
function hesapMakinesi(islemler) {
let sonuc = 0;
let gecmis = [];
for (const islem of islemler) {
// Geçersiz işlem kontrolü
if (!islem || typeof islem.deger !== "number") {
console.warn(`⚠️ Geçersiz işlem atlandı:`, islem);
continue;
}
// Sıfıra bölme kontrolü
if (islem.operator === "/" && islem.deger === 0) {
console.error("❌ Sıfıra bölme yapılamaz!");
continue;
}
let oncekiSonuc = sonuc;
switch (islem.operator) {
case "+":
sonuc += islem.deger;
break;
case "-":
sonuc -= islem.deger;
break;
case "*":
sonuc *= islem.deger;
break;
case "/":
sonuc /= islem.deger;
break;
case "=":
sonuc = islem.deger;
break;
default:
console.warn(`⚠️ Bilinmeyen operatör: ${islem.operator}`);
continue;
}
gecmis.push({
islem: `${oncekiSonuc} ${islem.operator} ${islem.deger} = ${sonuc}`,
sonuc: sonuc
});
}
// Geçmişi göster
console.log("\n📊 İşlem Geçmişi:");
for (const [index, kayit] of gecmis.entries()) {
console.log(` ${index + 1}. ${kayit.islem}`);
}
console.log(`\n🟰 Son sonuç: ${sonuc}`);
return sonuc;
}
// Kullanım
let islemler = [
{ operator: "=", deger: 100 },
{ operator: "+", deger: 50 },
{ operator: "*", deger: 2 },
{ operator: "-", deger: 75 },
{ operator: "/", deger: 5 },
{ operator: "/", deger: 0 }, // Sıfıra bölme — atlanacak
{ operator: "%", deger: 3 }, // Bilinmeyen operatör — atlanacak
{ operator: "+", deger: 10 }
];
hesapMakinesi(islemler);Bu örnekte for...of, switch, if, continue, break ve hata kontrolünü bir arada görebilirsiniz.
Performans İpuçları
// 1. Döngü içinde .length'i cache'leyin (büyük diziler için)
const buyukDizi = new Array(1000000).fill(0);
// ❌ Her iterasyonda length hesaplanır
for (let i = 0; i < buyukDizi.length; i++) { /* ... */ }
// ✅ length bir kez hesaplanır
for (let i = 0, len = buyukDizi.length; i < len; i++) { /* ... */ }
// 2. Gereksiz hesaplamaları döngü dışına alın
// ❌ Her iterasyonda aynı hesaplama
for (let i = 0; i < dizi.length; i++) {
let sinir = Math.floor(maxDeger / 2); // Her seferinde hesaplanıyor
// ...
}
// ✅ Bir kez hesapla
let sinir = Math.floor(maxDeger / 2);
for (let i = 0; i < dizi.length; i++) {
// sinir zaten hazır
}Özet
🔹 if/else en temel karar yapısıdır.
else ifzincirlerinde en spesifik koşul en üstte olmalıdır🔹 switch belirli değerlere karşı eşitlik kontrolü için idealdir,
breakyazmayı unutmayın (fall-through tuzağı)🔹 for döngüsü sayısal iterasyon için, for...of dizi/string değerleri için, for...in nesne anahtarları için kullanılır
🔹 while koşul doğru olduğu sürece, do...while en az bir kez çalıştırıp sonra koşul kontrol eder
🔹 break döngüyü tamamen sonlandırır, continue mevcut adımı atlar — ikisi de döngü kontrolü için kritiktir
🔹
for...indizilerle kullanmayın — string index ve prototype sorunlarına yol açar; dizilerdefor...oftercih edin
AI Asistan
Sorularını yanıtlamaya hazır