JavaScript Java Eğitim Seti
JavaScript Kapsamlı Eğitim
1.1 Giriş ve Temeller: JS Nedir?
JavaScript (JS) sihirli bir dildir! Bir web sayfasını durağan (sadece yazı ve resim) olmaktan çıkarıp, hareketli ve etkileşimli hale getiren bir yazılım dilidir. Tıpkı bir kukla tiyatrosunda, ipleri çekerek kuklalara hayat veren kişi gibi düşünebilirsiniz.
JS Ne İşe Yarar? (Çevremizdeki Örnekler)
- Bir butona tıkladığınızda rengin değişmesi.
- Bir form doldurduğunuzda hatalı alanları size hemen söylemesi.
- Sayfayı aşağı kaydırdıkça yeni içeriğin yüklenmesi.
JS Nasıl Kullanılır?
JS kodlarını HTML sayfamıza üç yolla ekleyebiliriz:
- Harici (External) Dosya : En düzenli yöntemdir. Tüm JS kodlarımızı
app.jsgibi ayrı bir dosyada tutarız. HTML dosyasının en altına bu dosyayı bağlarız.<script src="app.js"></script> - Dahili (Internal) : JS kodunu doğrudan HTML içindeki
<script></script>etiketleri arasına yazarız.
İlk Adım: Konsola Yazmak (console.log)
Yazdığımız kodların çalışıp çalışmadığını, içindeki değerlerin ne olduğunu görmek için bir "kontrol paneli" kullanırız. Buna Konsol (Console) denir.
console.log("Merhaba Dünya!");
console.log(5 + 3); // 8 sonucunu verir
1.2 Değişkenler ve Veri Türleri
Değişkenler, bir bilgisayarın hafızasında bilgi (veri) saklamak için kullandığımız isimli kutulardır. Bu kutuların içine koyduğumuz şey, yani veri türü, önemlidir.
Değişken Tanımlama Kutuları: let, const
let: Değeri sonradan değişebilen kutudur. Adı üstünde: "İzin ver, değişebilir."let yas = 10;yas = 11; // Değeri sorunsuz değişti.const: Değeri asla değişmeyecek olan kilitli kutudur. Bu, bir sabittir.const PI_SAYISI = 3.14;PI_SAYISI = 4; // Hata verir! Değiştirilemez.var: Eski usul kutu. Artık pek kullanmıyoruz çünkü kafa karıştırıcı olabiliyor. let ve const modern JS'in standardıdır.
Önemli Veri Türleri (Kutunun İçindeki Şey)
- String (Metin) : Kelimeler, cümleler. Her zaman tırnak işaretleri içinde yazılır.
let isim = "Ahmet"; - Number (Sayı) : Matematiksel işlemler yapabildiğimiz rakamlar. Tırnak içine alınmaz.
let puan = 95; - Boolean (Mantıksal) : Sadece iki değeri vardır: true (doğru) veya false (yanlış). Sorulara cevap verir.
let isikAcikMi = true;
1.3 Operatörler: İşlem Yapıları
Operatörler, değişkenler ve değerler üzerinde işlemler yapmamızı sağlayan sembollerdir. Tıpkı bir hesap makinesi gibi!
1. Aritmetik Operatörler (Matematik İşlemleri)
+(Toplama),-(Çıkarma)*(Çarpma),/(Bölme)%(Mod alma - Kalanı bulma):10 % 3sonucu 1'dir.**(Üs alma):2 ** 3sonucu 8'dir.
2. Karşılaştırma Operatörleri (Soru Sorma)
Bu operatörler her zaman true ya da false (Boolean) cevabı verir.
>(Büyüktür),<(Küçüktür)>=(Büyük Eşit),<=(Küçük Eşit)
KRİTİK FARK: Eşitlik Kontrolü
- Gevşek Eşitlik (
==) : Sadece değerlere bakar, tipleri umursamaz.10 == '10'sonuç: true (Çünkü değer aynı.) - Katı Eşitlik (
===) : Hem değere hem de veri tipine bakar. Bu, en güvenilir kontroldür ve her zaman bunu kullanmalıyız.10 === '10'sonuç: false (Çünkü biri sayı, diğeri metin.)
1.4 Koşullar ve Karar Yapıları
Hayatımızda sürekli karar veririz: "Yağmur yağıyorsa şemsiye al, yoksa alma." Kodlamada da aynı mantık vardır. Programın hangi yolu izleyeceğine karar veririz.
if, else if, else (Eğer, Yoksa Eğer, Yoksa)
Belirli bir koşul doğruysa (true) bir kod bloğunu çalıştırırız.
let havaDurumu = "Güneşli";
if (havaDurumu === "Yağmurlu") {
console.log("Şemsiyeni al!");
} else if (havaDurumu === "Karlı") {
console.log("Montunu giy ve eldiven tak!");
} else {
console.log("Hava güzel, dışarı çıkabilirsin.");
}
// Çıktı: Hava güzel, dışarı çıkabilirsin.
Ternary Operator (Tek Satırlık Koşul)
Çok basit if/else yapılarını tek bir satırda yazmanın kısa yolu.
let yas = 15;
let durum = (yas >= 18) ? "Ehliyet alabilir." : "Daha beklemesi gerek.";
// Çıktı: Daha beklemesi gerek.
1.5 Döngüler: Tekrarlayan İşlemler
Aynı işlemi defalarca yapmak istediğimizde, kodu tekrar tekrar yazmak yerine döngüleri kullanırız. Tıpkı bir şarkının nakaratının tekrar etmesi gibi.
for Döngüsü (Belirli Bir Kez Tekrar)
Kaç kez tekrar edeceğini bildiğimizde kullanılır.
// 1'den 5'e kadar sayma
for (let i = 1; i <= 5; i++) {
console.log("Sayı: " + i);
}
/*
Çıktı:
Sayı: 1
Sayı: 2
...
Sayı: 5
*/
while Döngüsü (Koşul Doğru Olduğu Sürece Tekrar)
Bir koşul doğru olduğu sürece döngü çalışmaya devam eder.
let canSayisi = 3;
while (canSayisi > 0) {
console.log("Oyunda " + canSayisi + " can kaldı.");
canSayisi = canSayisi - 1; // Canı 1 azalt
}
// Çıktı: Oyunda 3 can kaldı. / Oyunda 2 can kaldı. / Oyunda 1 can kaldı.
break ve continue (Döngüyü Kontrol Etme)
break: Döngüyü tamamen durdurur ve döngüden çıkar. (Acil çıkış).continue: Döngüdeki o anki adımı atlar ve bir sonraki adıma geçer. (Bu adımı boşver).
1.6 Fonksiyonlar: Kod Tekrarını Önleme
Fonksiyonlar, bir görevi yerine getiren, bir araya toplanmış kod bloklarıdır. Tıpkı bir mutfaktaki yemek tarifi gibi. Tarife bir kez yazılır, ama istediğiniz zaman kullanabilirsiniz.
Fonksiyon Tanımlama (Tarifi Yazma)
Fonksiyonlar, içeriği tanımladıktan sonra çağrılana kadar çalışmazlar.
// Bir fonksiyon tanımladık. (Tarifi yazdık)
function selamVer(isim) {
console.log("Merhaba, " + isim + "! Nasılsın?");
}
// Fonksiyonu çağırdık. (Tarifi uyguladık)
selamVer("Ayşe"); // Çıktı: Merhaba, Ayşe! Nasılsın?
selamVer("Mehmet"); // Çıktı: Merhaba, Mehmet! Nasılsın?
Geri Dönüş Değeri (Return)
Fonksiyonlar, işlerini bitirdikten sonra bir sonuç (değer) geri gönderebilir. Buna return denir.
function karesiniHesapla(sayi) {
return sayi * sayi; // Sonucu dışarı gönder
}
let sonuc = karesiniHesapla(4);
console.log(sonuc); // Çıktı: 16
Arrow Functions (Modern Kısa Yol)
ES6 ile gelen, fonksiyonları daha kısa yazmanın popüler yoludur.
const carp = (sayi1, sayi2) => {
return sayi1 * sayi2;
};
// Veya daha da kısası:
const topla = (a, b) => a + b;
1.7 Diziler (Arrays): Sıralı Koleksiyonlar
Diziler, aynı tipte veya farklı tipte birden fazla veriyi sıralı bir şekilde tek bir yerde tutmamızı sağlayan büyük kutulardır. Tıpkı bir bakkaldaki raflar gibi, her ürünün bir sırası (index'i) vardır.
Dizi Oluşturma ve Erişim
- Diziler köşeli parantez (
[ ]) ile tanımlanır. - Dizi elemanları 0'dan başlayarak numaralandırılır. (Buna index denir).
let meyveler = ["Elma", "Muz", "Portakal", "Çilek"];
// Erişim
console.log(meyveler[0]); // Çıktı: Elma (İlk eleman)
console.log(meyveler[2]); // Çıktı: Portakal
Temel Dizi Metotları
push(): Dizinin sonuna yeni eleman ekler.pop(): Dizinin sonundaki elemanı çıkarır.length: Dizinin kaç elemanı olduğunu söyler.console.log(meyveler.length); // Çıktı: 4
Diziler, özellikle döngülerle birleştiğinde çok güçlüdür. Örneğin, bir for döngüsü ile tüm meyvelerin üzerinden geçebiliriz.
1.8 Nesneler (Objects): Anahtar-Değer Çiftleri
Nesneler, birbiriyle ilgili bilgileri tutan, ancak sırasıyla değil, isimleriyle (anahtarlarıyla) eriştiğimiz karmaşık kutulardır. Tıpkı bir kimlik kartı gibi, her bilginin bir başlığı vardır.
Nesne Oluşturma ve Erişim
- Nesneler süslü parantez (
{ }) ile tanımlanır. - Her bilgi bir Anahtar (Key) ve Değer (Value) çifti şeklindedir.
let araba = {
marka: "Toyota",
model: 2022,
renk: "Kırmızı",
calistir: function() {
return "Vınn Vınn!";
}
};
Özelliklere Erişim
- Nokta Notasyonu (Önerilen):
araba.marka// Çıktı: "Toyota" - Köşeli Parantez Notasyonu:
araba["renk"]// Çıktı: "Kırmızı"
this Anahtar Kelimesi
Bir nesnenin içindeki fonksiyon (metot), o nesnenin kendi özelliklerine erişmek istediğinde this kelimesini kullanır. this, "bu nesne" anlamına gelir.
let kedi = {
isim: "Pamuk",
sesCikar: function() {
console.log(this.isim + " miyavlıyor.");
}
};
kedi.sesCikar(); // Çıktı: Pamuk miyavlıyor.
2.1 DOM Manipülasyonu: Web Sayfasına Hayat Vermek
DOM (Document Object Model), web sayfanızdaki her şeyi (başlıkları, resimleri, butonları) temsil eden bir ağaç yapısıdır. JS ile bu ağacın dallarını, yapraklarını (yani HTML elementlerini) değiştirebiliriz. Bu sayede sayfanız durağanlıktan çıkar ve canlı hale gelir.
1. Element Seçme (Ağaçtaki Yaprağı Bulma)
Bir HTML elementine ulaşmak için onun kimliğini (ID, Class veya etiket adını) kullanırız:
document.getElementById('id-adi'): En hızlı ve en kesin yol. Tek bir eleman döner.document.querySelector('.class-adi'): Class, ID, etiket veya herhangi bir CSS seçicisiyle ilk eşleşen elemanı bulur. (Çok kullanışlıdır)document.querySelectorAll('a'): Tüm<a>(link) etiketlerini bulur.
const baslik = document.querySelector('h1');
console.log(baslik.textContent); // Başlığın içindeki metni gösterir.
2. İçerik ve Stil Değiştirme (Yaprağın Rengini Değiştirme)
- İçerik:
baslik.textContent = 'Yeni Başlık!'; - HTML İçeriği:
div.innerHTML = '<b>Kalın Yazı</b>'; - Stil (CSS):
baslik.style.color = 'blue'; - Sınıf Ekleme/Silme:
baslik.classList.add('buyuk-yazi');(CSS sınıfı ekleyerek stili değiştirmenin en iyi yolu)
3. Yeni Elementler Oluşturma
JS ile sıfırdan bir HTML elemanı yaratıp sayfaya ekleyebiliriz:
- Oluştur:
const yeniPara = document.createElement('p'); - İçerik ver:
yeniPara.textContent = 'Yeni bir paragraf.'; - Ekle:
document.body.appendChild(yeniPara);(Body etiketinin sonuna ekler)
2.2 Event Handling: Etkileşime Tepki Vermek
Event (Olay), bir web sayfasında gerçekleşen her şeydir. Fare tıklaması, klavyede bir tuşa basılması, sayfanın yüklenmesi gibi. Event Handling ise bu olaylara JS ile tepki vermektir.
Olay Dinleyicisi (addEventListener)
Bir elementin bir olayı dinlemesini ve o olay gerçekleştiğinde bir fonksiyonu çalıştırmasını sağlarız.
const buton = document.querySelector('#tikla-beni');
// Buton "tıklandığında" (click olayı), içindeki fonksiyon çalışır.
buton.addEventListener('click', function() {
alert('Butona tıkladın!');
});
Yaygın Kullanılan Olaylar (Events)
click: Fare ile tıklama (En sık kullanılan).mouseover: Fare elementin üzerine geldiğinde.keydown: Klavyede bir tuşa basıldığında.submit: Bir form gönderildiğinde.
Event Objesi
Olay gerçekleştiğinde, JavaScript bize o olay hakkında detaylı bilgi içeren bir Event objesi gönderir. Bu objeyi kullanarak olayı kontrol edebiliriz.
e.target: Olayın gerçekleştiği HTML elementini verir.e.preventDefault(): Tarayıcının varsayılan hareketini (örneğin, bir linke tıklayınca sayfayı değiştirmeyi) durdurur. Form göndermeyi engellemek için çok önemlidir.
2.3 Zamanlama Fonksiyonları: Gecikmeli İşlemler
Bu fonksiyonlar, kodu hemen değil, belirli bir süre sonra veya tekrar eden aralıklarla çalıştırmamızı sağlar. Tıpkı bir alarm kurmak gibi!
setTimeout (Alarm Kurma)
Kodu, sadece bir kez, belirtilen milisaniye (saniyenin binde biri) cinsinden süre geçtikten sonra çalıştırır.
// 3 saniye (3000 milisaniye) sonra çalışacak
setTimeout(function() {
console.log("3 saniyelik geri sayım bitti!");
}, 3000);
setInterval (Tekrarlayan Alarm)
Kodu, belirtilen sürede tekrar tekrar çalıştırmaya devam eder. Örneğin, saniye sayacı yapmak için idealdir.
let saniye = 0;
// Her 1 saniyede (1000 milisaniye) bir çalışır
const sayacID = setInterval(function() {
saniye++;
console.log("Geçen saniye: " + saniye);
if (saniye === 5) {
clearInterval(sayacID); // 5 saniye sonra durdur!
}
}, 1000);
Durdurma (Temizleme) Fonksiyonları
Zamanlayıcılar başladıktan sonra, onları durdurmak için bir ID kullanırız:
clearTimeout(ID): Tek seferlik olanı durdurur.clearInterval(ID): Tekrarlayanı durdurur.
2.4 Hata Yönetimi: Programı Korumak
Bazen kodumuzun içinde, bizim kontrolümüz dışındaki nedenlerle (örneğin internet bağlantısının kopması veya yanlış veri girilmesi) hatalar oluşabilir. Hata Yönetimi, bu hataları programın çökmesini engellemeden "yakalama" ve kibarca ele alma sanatıdır.
try...catch Bloğu (Hata Yakalama)
Bir dedektif gibi şüpheli kodu try bloğuna koyarız. Eğer bu kod çalışırken bir sorun çıkarsa, program çökmez; sorun hemen catch bloğuna fırlatılır ve biz de burada hatayı yakalarız.
try {
// Şüpheli kod buraya
let sonuc = degiskenYok / 10; // Hata! degiskenYok tanımlı değil.
console.log(sonuc);
} catch (hata) {
// Hata oluşursa burası çalışır
console.error("Bir sorun oluştu:", hata.message); // Hatayı kibarca yazdırırız.
// Kullanıcıya bir mesaj gösterebiliriz.
} finally {
// Hata olsun veya olmasın, burası her zaman çalışır. (Örn: Bağlantıyı kapatma)
console.log("Deneme tamamlandı.");
}
Hata Türleri
- Syntax Error: Yazım hatası (Noktalı virgülü unutmak, parantezi kapatmamak). JS bu hatada hemen durur.
- Reference Error: Tanımlanmamış bir değişkeni kullanmaya çalışmak. (Yukarıdaki örnek gibi).
- Type Error: Bir veri türünün izin vermediği bir işlemi yapmak. (Örn: Sayı olmayan bir şeye
.lengthuygulamak).
2.5 ES6+ Özellikleri: JavaScript'in Yenilenmesi
ES6 (ECMAScript 2015) ile birlikte JavaScript, daha okunabilir, daha kısa ve daha modern bir dil haline geldi. Artık daha az kodla daha çok iş yapabiliyoruz!
1. Arrow Functions (Ok Fonksiyonları)
Daha kısa fonksiyon yazımı. (Bkz. 1.6)
// Eski:
// function topla(a, b) { return a + b; }
// Yeni (Arrow):
const topla = (a, b) => a + b;
2. Template Literals (Şablon Dizeleri)
Metin (string) içinde değişken kullanmanın ve çok satırlı metin yazmanın süslü yolu. Normal tırnak yerine ters tırnak (`) kullanılır.
const isim = "Deniz";
const mesaj = `Merhaba ${isim},
Bugün hava çok güzel!`; // ${} içine değişkeni koyarız.
console.log(mesaj);
3. Destructuring (Yapı Bozma)
Diziler veya nesneler içindeki değerleri tek bir satırda kolayca değişkenlere atama.
// Nesnede
const ogrenci = { adi: "Can", yasi: 12 };
const { adi, yasi } = ogrenci;
console.log(adi); // Çıktı: Can
// Dizide
const renkler = ["Kırmızı", "Mavi"];
const [birinciRenk, ikinciRenk] = renkler;
console.log(birinciRenk); // Çıktı: Kırmızı
4. Spread Operator (Yayılma Operatörü - ...)
Dizileri veya nesneleri kopyalamak veya birleştirmek için kullanılır. Üç nokta (...) ile gösterilir.
const eskiDizi = [1, 2];
const yeniDizi = [...eskiDizi, 3, 4]; // YeniDizi: [1, 2, 3, 4]
2.6 JSON & Veri İşleme
JSON (JavaScript Object Notation), bilgisayarlar arasında (örneğin sunucu ile tarayıcı) veri alışverişi yapmak için kullanılan evrensel bir dildir. Tıpkı kargo paketleri gibi, tüm bilgiyi düzenli bir formatta taşır.
JSON Nedir?
- JSON, aslında JavaScript'teki nesne (Object) yapısına çok benzer.
- Tek kural: Tüm anahtarlar (keys) çift tırnak (
" ") içinde olmalıdır.
// Bu bir JSON metnidir (String)
const jsonMetin = '{"isim": "Ege", "puan": 85, "aktif": true}';
JSON İşlemleri: Çeviri (Parse/Stringify)
JSON metni ile JavaScript nesnesi arasında sürekli çeviri yapmamız gerekir:
JSON.parse(): JSON metnini alıp, JS'in anlayacağı bir Nesneye (Object) çevirir. (Kargoyu açma).JSON.stringify(): JS Nesnesini alıp, göndermeye hazır bir JSON metnine (String) çevirir. (Kargoyu paketleme).
// 1. Çözümleme (Parse)
const ogrenciNesnesi = JSON.parse(jsonMetin);
console.log(ogrenciNesnesi.isim); // Çıktı: Ege (Artık nesne olarak kullanabiliriz)
// 2. Paketleme (Stringify)
const yeniJSON = JSON.stringify({ sehir: "Ankara", plaka: 6 });
console.log(yeniJSON); // Çıktı: '{"sehir":"Ankara","plaka":6}' (Göndermeye hazır metin)
Tarayıcı Depolama (Web Storage)
Verileri kullanıcının tarayıcısında saklamak için kullanılır.
localStorage: Tarayıcı kapansa bile veriyi kalıcı olarak saklar.sessionStorage: Tarayıcı kapatılana kadar veriyi saklar.
Bu depolama alanları sadece metin (string) saklayabildiği için, karmaşık verileri (Nesne veya Dizi) saklarken önce JSON.stringify() ile metne çevirmeliyiz.
3.1 Asenkron Programlama: Aynı Anda Birden Çok İş
JavaScript tek bir aşçı gibi çalışır. Bir işi bitirmeden diğerine geçemez (Senkron). Ama bazı işler (mesela internetten veri indirmek) çok uzun sürer. Eğer bu işi beklersek, sayfa donar kalır.
Asenkron Programlama, uzun süren işleri "arka plana" atarak, sayfanın donmasını engellemeyi ve iş bitince bize haber vermeyi sağlar.
1. Promise (Söz Verme)
Promise, bir işin gelecekte ya başarıyla tamamlanacağına (sözü tutma) ya da bir hatayla sonuçlanacağına (sözü tutamama) dair bir sözdür.
.then(): İş başarılı olursa (veri gelirse) ne yapılacağını belirleriz..catch(): İş hata verirse ne yapılacağını belirleriz.
veriAl(url)
.then(gelenVeri => {
console.log("Veri başarıyla geldi:", gelenVeri);
})
.catch(hata => {
console.error("Veri alma hatası:", hata);
});
2. async/await (Sözleri Beklemek)
Promise yapılarını daha kolay ve okunaklı hale getiren modern bir yöntemdir. Sanki asenkron kod yazmıyormuşuz gibi görünür.
async: Bu kelime bir fonksiyonun başına konulduğunda, o fonksiyonun Promise döndüreceğini belirtir. (Bu fonksiyon uzun sürebilir, haberiniz olsun.)await: Sadece birasyncfonksiyonun içinde kullanılabilir. Bir Promise'in sonucunu bekle ve sonuç gelince devam et demektir.
async function veriyiGetir() {
try {
const cevap = await fetch('api/kullanicilar'); // Bekle!
const veri = await cevap.json(); // Tekrar bekle!
console.log("Tüm veriler hazır:", veri);
} catch (hata) {
console.error("Bir aksilik oldu:", hata);
}
}
veriyiGetir();
3.2 Nesne Yönelimli Programlama (OOP): Gerçek Hayat Modellemesi
OOP (Object-Oriented Programming), kodumuzu gerçek dünyadaki nesneler gibi düzenleme yöntemidir. Tıpkı bir robotun veya bir arabanın özelliklerini ve yapabildiklerini modellemek gibi.
1. Class (Sınıf)
Class, nesneler oluşturmak için kullanılan bir kalıp veya plandır. Tıpkı bir araba fabrikasındaki temel tasarım planı gibi.
class Araba {
// Kurucu metot: Nesne ilk oluşturulduğunda çalışır.
constructor(marka, renk) {
this.marka = marka; // Özellik (Property)
this.renk = renk;
}
// Metot (Yapılabilen iş)
sur() {
console.log(this.renk + " " + this.marka + " gidiyor.");
}
}
2. Nesne (Object)
Class kalıbından oluşturulan gerçek örneklerdir.
// Yeni nesneler (arabalar) oluşturma
const araba1 = new Araba("Honda", "Mavi");
const araba2 = new Araba("Tofaş", "Kırmızı");
araba1.sur(); // Çıktı: Mavi Honda gidiyor.
araba2.sur(); // Çıktı: Kırmızı Tofaş gidiyor.
3. Kalıtım (Inheritance)
Bir Class'ın (Çocuk), başka bir Class'ın (Ebeveyn) tüm özelliklerini ve metotlarını miras almasıdır. Bu, kodu tekrar yazmaktan kurtarır. extends anahtar kelimesi kullanılır.
class SporAraba extends Araba {
hizlan() {
console.log(this.marka + " çok hızlı hızlanıyor!");
}
}
const ferrari = new SporAraba("Ferrari", "Sarı");
ferrari.sur(); // Miras alınan metot
ferrari.hizlan(); // Yeni eklenen metot
3.3 Fonksiyonel Programlama (FP): Saf Kod Yazmak
Fonksiyonel Programlama (FP), kodlamayı matematiksel fonksiyonlar gibi ele alan bir yaklaşımdır. Amaç, kodu daha güvenilir, daha az hatalı ve test etmesi daha kolay hale getirmektir.
1. Saf Fonksiyonlar (Pure Functions)
FP'nin kalbidir. Saf fonksiyonlar, tıpkı matematik gibi çalışır:
- Aynı girdiyi verirseniz, her zaman aynı çıktıyı verir.
- Dışarıdaki hiçbir şeyi değiştirmezler (Dışarıdaki değişkeni, DOM'u vb. etkilemezler).
Örnek (Saf):
function ikiyleCarp(sayi) {
return sayi * 2; // Sadece girdiye odaklanıyor.
}
Örnek (Saf Değil):
let KDV = 0.20; // Dışarıdan gelen değişken (değişebilir)
function vergiyiEkle(fiyat) {
return fiyat * (1 + KDV); // Dışarıdaki bir şeye bağımlı
}
2. Yüksek Mertebeli Fonksiyonlar (Higher-Order Functions - HOF)
Fonksiyonların girdisi (parametre) olarak başka bir fonksiyonu kabul eden veya çıktı (return) olarak bir fonksiyon döndüren fonksiyonlardır. Dizilerdeki meşhur metotlar HOF'tur:
.map(): Dizideki her elemanı alıp değiştirerek yeni bir dizi oluşturur..filter(): Dizideki elemanlardan bir koşulu sağlayanları alarak yeni bir dizi oluşturur..reduce(): Dizideki tüm elemanları tek bir değere indirir (toplama, çarpma vb.).
const sayilar = [1, 2, 3, 4];
// Map: Her sayıyı karesine çevir.
const kareler = sayilar.map(sayi => sayi * sayi);
console.log(kareler); // Çıktı: [1, 4, 9, 16]
3.4 Modüler Yapı: Kodları Düzenlemek
Büyük projelerde tüm kodları tek bir dosyada tutmak çok karmaşıktır. Modüller, kodumuzu küçük, yönetilebilir ve bağımsız parçalara ayırmamızı sağlar. Tıpkı bir yapbozun parçaları gibi.
Import ve Export (Al ve Ver)
Modüllerin iki temel işlevi vardır:
export: Bir modülden dış dünyaya (başka bir dosyaya) hangi fonksiyonları veya değişkenleri vereceğimizi belirtiriz.import: Başka bir modülün bize verdiği şeyleri kendi dosyamızda kullanmak için alırız.
Dosya 1: hesaplama.js (Export Eden)
// Bu fonksiyonu dış dünyaya veriyoruz
export const PI = 3.14;
export function topla(a, b) {
return a + b;
}
// Varsayılan (Default) export: Dosya başına bir tane olur
export default function carpma(a, b) {
return a * b;
}
Dosya 2: app.js (Import Eden)
// Named Export'ları {} içine alırız
import { PI, topla } from './hesaplama.js';
// Default Export'ı istediğimiz isimle alırız
import carp from './hesaplama.js';
console.log(topla(5, 5)); // Çıktı: 10
console.log(carp(5, 5)); // Çıktı: 25
Neden Modül Kullanılır? Kod tekrarını azaltır, kodun anlaşılırlığını artırır ve hataların sadece tek bir bölümde kalmasını (izolasyon) sağlar.
3.5 Regular Expressions (Regex): Metin Dedektifliği
Regular Expressions (Regex) ya da Türkçe adıyla Düzenli İfadeler, metinler içinde karmaşık desenleri (pattern) aramak, bulmak ve değiştirmek için kullanılan özel bir dildir. Tıpkı bir dedektifin ipuçlarını araması gibi, Regex ile metin içindeki belirli yapıları kolayca tespit edebiliriz.
Regex Ne İşe Yarar?
- Bir metnin geçerli bir e-posta adresi olup olmadığını kontrol etmek.
- Bir metnin içinden tüm telefon numaralarını bulmak.
- Bir formdaki TC Kimlik No formatının doğru olup olmadığını denetlemek.
Temel Regex Kuralları
Regex ifadeleri iki eğik çizgi (/ /) arasına yazılır.
/a/: Sadece 'a' harfini arar./[0-9]/: Herhangi bir rakamı (0'dan 9'a) arar. (Kısa yolu:\d)/[A-Z]/: Herhangi bir büyük harfi arar.+: Kendisinden önceki karakterin bir veya daha fazla kez tekrar ettiğini belirtir.
Regex Uygulamaları (String Metotları)
Regex ifadelerini genellikle String veri tipinin metotlarıyla birlikte kullanırız:
.test(): Metin bu desene uyuyor mu? (Cevap: true/false)..match(): Metin içinde bu desene uyan yerleri bul..replace(): Desene uyan yerleri başka bir şeyle değiştir.
const metin = "Bende 20 elma ve 15 armut var.";
const desen = /\d+/g; // \d (rakam) + (bir veya daha fazla) g (global, yani hepsini bul)
const rakamlar = metin.match(desen);
console.log(rakamlar); // Çıktı: ["20", "15"]
const yeniMetin = metin.replace(desen, 'X');
console.log(yeniMetin); // Çıktı: "Bende X elma ve X armut var."
4.1 İleri Asenkron Yapılar: Detaylı Akış Kontrolü
Temel Promise ve `async/await` yapısını öğrendik. Uzmanlık seviyesinde, birden çok asenkron görevi aynı anda yönetme ve JavaScript'in arkasındaki gizli mekanizmayı (Event Loop) anlamamız gerekir.
1. Event Loop (Olay Döngüsü)
JavaScript'in kalbi, onun tek iş parçacıklı (Single-Threaded) olmasına rağmen neden donmadığını açıklar. Event Loop, uzun süren asenkron görevleri (API çağrıları, `setTimeout` gibi) ana iş parçacığından ayırarak, ana iş parçacığının kullanıcı arayüzü (UI) ve senkron kodları çalıştırmaya devam etmesini sağlar. Asenkron işler bitince, sonuçlar Callback Queue'ya girer ve Event Loop onları ana iş parçacığı boş olduğunda sırayla alır.
- Call Stack (Çağrı Yığını): Senkron kodların çalıştığı yer.
- Web/Node APIs: Tarayıcının sağladığı asenkron görevleri çalıştıran yer (DOM, `fetch`, `setTimeout`).
- Callback Queue (Geri Çağırım Kuyruğu): Asenkron görevler bittiğinde bekleyen fonksiyonların sıraya girdiği yer.
- Event Loop: Stack boşalınca, Queue'dan görev alıp Stack'e gönderen sihirli mekanizma.
2. Birden Çok Promise'i Yönetme
Bazen, bir sonraki adıma geçmeden önce birden fazla verinin internetten gelmesini beklememiz gerekir.
Promise.all(): Bir dizi Promise alır ve tümü başarıyla tamamlandığında sonuçları bir dizi olarak döndürür. Eğer içlerinden biri bile hata verirse, hepsi reddedilir (hata verir).const p1 = fetch('api/kullanici'); const p2 = fetch('api/postlar'); Promise.all([p1, p2]) .then(sonuclar => { // [kullaniciCevabi, postlarCevabi] console.log("Kullanıcı ve postlar aynı anda geldi."); }) .catch(hata => console.error("Bir veri bile gelmedi, hata:", hata));Promise.race(): Bir dizi Promise alır ve içlerinden ilk biten (hata veya başarı) ne olursa olsun onu döndürür. Genellikle bir zaman aşımı (timeout) belirlemek için kullanılır.const anaVeri = fetch('api/hizli-veri'); const zamanAsimi = new Promise((_, reject) => setTimeout(() => reject(new Error('Zaman aşımı!')), 5000)); Promise.race([anaVeri, zamanAsimi]) .then(veri => console.log("Veri 5 saniyeden önce geldi.")) .catch(hata => console.error(hata)); // Ya zaman aşımı ya da fetch hatasıPromise.allSettled(): Tüm promise'ler bitene kadar bekler (başarılı veya hatalı fark etmez). Her birinin sonucunu (status ve value/reason) içeren bir dizi döndürür. Bu, her işin sonucunu bilmek istediğimizde çok kullanışlıdır.
3. Microtask Queue (Mikro Görev Kuyruğu)
Promise'lerin `then` ve `catch` blokları, Event Loop'un baktığı ana kuyruk olan Callback Queue'ya değil, daha acil olan Microtask Queue'ya girer. Bu, Microtask'ların (Promise'ler) bir sonraki event loop döngüsünden önce her zaman işlenmesi gerektiği anlamına gelir. Yani, Promise'ler `setTimeout`'tan daha hızlı çalışır.
4.2 Bellek Yönetimi ve Performans: Hız ve Verimlilik
Büyük ve karmaşık uygulamalarda, kodun hızlı ve akıcı çalışması için bellek yönetimini anlamak kritiktir. Kötü kod, bilgisayarın hafızasını (RAM) doldurur ve uygulamayı yavaşlatır.
1. Garbage Collection (Çöp Toplayıcı)
JavaScript'te belleği manuel olarak yönetmeyiz. Tarayıcı veya Node.js motoru (V8), oluşturduğumuz nesneleri ve değişkenleri otomatik olarak temizler. Buna Çöp Toplama (Garbage Collection) denir.
Çöp Toplayıcı şu kurala bakar: Ulaşılabilirlik (Reachability). Bir nesneye artık kod içinde hiçbir yerden ulaşılamıyorsa, o nesne "çöp" kabul edilir ve bellekten silinir.
- Bellek Sızıntısı (Memory Leak): Eğer kullanmadığımız bir nesneyi bir şekilde tutmaya (örneğin, bir dizi içinde, veya DOM'dan silinmiş bir elemente referansla) devam edersek, çöp toplayıcı onu temizleyemez. Bu, bellek sızıntısına ve uygulamanın yavaşlamasına neden olur.
2. Performans İpuçları (Hızlandırma Teknikleri)
- DOM Manipülasyonunu Azaltma: DOM'a her erişim (bir elementin rengini değiştirme, ekleme) yavaştır. Birden fazla değişiklik yapacaksak, elementi hafızada oluşturup tek seferde DOM'a eklemek performansı artırır (document fragment kullanmak gibi).
- Debounce ve Throttle: Çok sık tetiklenen olayları (scroll, pencere yeniden boyutlandırma, tuş basma) kontrol altına almak için kullanılır:
- Debounce: Fonksiyonu, sadece olaylar belirli bir süre durduktan sonra çalıştır. (Örn: Arama kutusuna yazarken, yazmayı bitirince ara.)
- Throttle: Fonksiyonu, belirli bir zaman aralığında (örneğin 100 ms'de bir) en fazla bir kez çalıştır. (Örn: Scroll olayında akıcılığı koruyarak 100ms'de bir loglama yap.)
- Micro-optimizasyonlar: Eski döngüler (
for) çoğu zaman modern metotlardan (`forEach`, `map`) biraz daha hızlıdır, ancak okunabilirlik genelde önceliklidir. Performans darboğazı oluşan yerlerde bu tür mikro optimizasyonlara başvurulabilir. - Big O Analizi: Bir algoritmanın giriş verisi büyüdükçe ne kadar yavaşlayacağını (zaman karmaşıklığını) ölçme tekniği. Uzman bir geliştirici, kod yazmadan önce algoritmasının karmaşıklığını ($O(n^2)$, $O(n \log n)$, $O(n)$) düşünmelidir.
3. Büyük Veri Yapılarıyla Çalışma
Büyük veri kümeleriyle çalışırken `Map` ve `Set` kullanmak, geleneksel nesne ve dizilere göre daha iyi performans verebilir. Özellikle `WeakMap` ve `WeakSet`, key'leri (anahtarları) çöp toplayıcının silmesine izin vererek bellek sızıntılarını engellemeye yardımcı olur.
// DOM elementi silindiğinde, WeakMap de referansı bırakır.
const elementHaritasi = new WeakMap();
elementHaritasi.set(myElement, { veri: 'gizli' });
// Eğer myElement DOM'dan silinirse, Garbage Collector WeakMap içindeki bu veriyi de temizleyebilir.
4.3 JavaScript Design Patterns: Kanıtlanmış Çözümler
Design Patterns (Tasarım Kalıpları), yazılım tasarımında tekrarlanan problemlere karşı geliştirilmiş, evrensel, kanıtlanmış çözümlerdir. Tıpkı bir mimarın farklı bina tipleri için farklı planlara sahip olması gibi, biz de kod problemlerini çözmek için bu kalıpları kullanırız. Bu, kodun daha esnek, okunabilir ve sürdürülebilir olmasını sağlar.
1. Singleton Pattern (Tekil Kalıp)
Bir Class'tan (sınıftan) sadece bir tane nesne (örnek) oluşturulmasını garanti eden kalıptır. Bu, örneğin bir veritabanı bağlantısı veya bir ayar yöneticisi gibi global olarak tek bir kaynağa ihtiyaç duyduğumuzda önemlidir.
class AyarYoneticisi {
constructor() {
if (AyarYoneticisi.instance) {
return AyarYoneticisi.instance; // Zaten varsa eskisini döndür
}
this.ayarlar = { tema: 'koyu' };
AyarYoneticisi.instance = this; // Yeni oluşturulan nesneyi sakla
}
getAyarlar() {
return this.ayarlar;
}
}
const ayar1 = new AyarYoneticisi();
const ayar2 = new AyarYoneticisi();
console.log(ayar1 === ayar2); // Çıktı: true (Aynı nesneyi kullanıyorlar)
2. Module Pattern (Modül Kalıbı)
Kodu özel (private) ve genel (public) bölümlere ayırarak veri gizliliği (encapsulation) sağlar. ES6 ile birlikte yerel `import/export` yapısı bu ihtiyacı karşılasa da, eski JS'te veya bazı yapılandırmalarda bu kalıp hala kullanılır.
Bir fonksiyonun içine kod yazarak ve sadece return ettiğimiz kısımları dışarı açarak yapılır.
const HesapMakinesiModulu = (function() {
let gizliSayi = 0; // PRIVATE: Dışarıdan erişilemez
function _sadeceToplama(a, b) { // PRIVATE fonksiyon
return a + b;
}
return { // PUBLIC API (Dışarıya Açılan Kısım)
topla: function(a, b) {
gizliSayi += a + b;
return _sadeceToplama(a, b);
},
gizliSayiyiGetir: function() {
return gizliSayi;
}
};
})();
console.log(HesapMakinesiModulu.topla(5, 3)); // Çıktı: 8
// console.log(HesapMakinesiModulu.gizliSayi); // Hata! Erişilemez.
3. Factory Pattern (Fabrika Kalıbı)
Nesne oluşturma sürecini, nesneyi kullanan kısımdan ayırır. Bir "fabrika" fonksiyonu oluştururuz ve bu fonksiyon, hangi tip nesne istediğimizi söyleyince onu bizim için üretir. Bu, kodun daha esnek olmasını sağlar; yeni bir nesne tipi eklediğimizde, fabrika fonksiyonu dışındaki kodları değiştirmemize gerek kalmaz.
4.4 Test Etme: Kodun Güvenilirliğini Sağlama
Profesyonel yazılım geliştirmede, bir kodun doğru çalıştığından emin olmanın tek yolu onu test etmektir. Test Etme, kodumuzu çalıştırıp, çıktının beklediğimiz gibi olup olmadığını kontrol eden otomatik süreçlerdir. Bu, hataları erken aşamada yakalar ve büyük projelerde güvenle değişiklik yapmamızı sağlar.
1. Test Türleri (Testin Kapsamı)
- Unit Test (Birim Testi): Uygulamanın en küçük parçalarını (tek bir fonksiyon, tek bir sınıf metodu) test ederiz. Amaç, her bir parçanın tek başına doğru çalıştığından emin olmaktır. (Örn: `topla(2, 3)` fonksiyonu gerçekten `5` döndürüyor mu?)
- Integration Test (Entegrasyon Testi): Birbiriyle konuşan birden fazla parçanın (iki farklı fonksiyon, veritabanı ve sunucu) birlikte doğru çalıştığından emin olmak için test ederiz. (Örn: Veri gönderildiğinde, önce sunucu onu işliyor, sonra veritabanına kaydediyor mu?)
- End-to-End (E2E) Test: Uygulamayı gerçek bir kullanıcı gibi test ederiz. Tarayıcıyı açar, butona tıklar, form doldurur ve sonuçları kontrol ederiz. (Örn: Bir kullanıcı kaydolma formunu doldurup, ödeme yapıp, onay e-postasını görebiliyor mu?)
2. Temel Test Kavramları (Jest/Mocha Örnekleri)
Testler genellikle bir test çerçevesi (Jest, Mocha) kullanılarak yazılır.
- describe(): Bir test grubunu tanımlar. (Örn: "Hesaplama Fonksiyonları Grubu")
- test() / it(): Tek bir test durumunu tanımlar. (Örn: "Pozitif sayıları toplamalı")
- expect(): Bir değerin beklenen bir şeye eşit olup olmadığını kontrol eder.
// Ornek bir Birim Testi (Jest ile) function topla(a, b) { return a + b; } test('topla fonksiyonu pozitif sayıları toplamalı', () => { expect(topla(5, 2)).toBe(7); // Beklentimiz: 5+2 = 7 olmalı });
3. Mocking ve Spying (Taklit Etme)
Birim testleri yaparken, test ettiğimiz fonksiyonun dış bağımlılıklarını (API çağrısı, veritabanı) gerçekte çalıştırmak istemeyiz. Bunun yerine, bu bağımlılıkları taklit ederiz (Mocking). Böylece testimiz sadece kendi fonksiyonumuza odaklanır ve daha hızlı çalışır.
- Mocking: Gerçek bir fonksiyonu veya modülü sahte bir sürümle değiştirme.
- Spying: Gerçek bir fonksiyonu kullanmaya devam ederken, onun kaç kez ve hangi argümanlarla çağrıldığını izleme.
4.5 TypeScript’e Giriş: JavaScript'e Süper Güçler
TypeScript (TS), JavaScript'in bir üst kümesidir. Yani, geçerli her JavaScript kodu aynı zamanda geçerli bir TypeScript kodudur. TS'in temel amacı, JS'e büyük bir güç katan Statik Tipleme (Static Typing) özelliğini eklemektir. Büyük ve karmaşık projelerde hataları çok erken aşamada bulmayı sağlar.
1. Statik Tipleme Nedir?
Geleneksel JavaScript, Dinamik Tipleme kullanır. Bu, bir değişkenin türünü (sayı, metin, nesne) program çalışana kadar bilmediği anlamına gelir. TypeScript ise türleri program çalışmadan önce (yazım aşamasında) kontrol eder.
- JS (Dinamik):
let x = 5; x = "merhaba";(Hata yok, program çalışırken sorun çıkarabilir.) - TS (Statik):
let x: number = 5; x = "merhaba";(Daha kod yazılırken hata verir: "Bir sayıya metin atayamazsın.")
2. Temel TypeScript Söz Dizimi
Değişkenin veya fonksiyonun yanına iki nokta üst üste (:) koyarak türünü belirtiriz.
// Değişken Tipi
let isim: string = "Ali";
let yas: number = 25;
let aktifMi: boolean = true;
// Fonksiyon Giriş ve Çıkış Tipi
function selamla(kullaniciAdi: string): string {
return "Selam, " + kullaniciAdi;
}
// Array Tipi
let sayilar: number[] = [1, 2, 3];
3. Interface'ler (Sözleşmeler)
Interface (Arayüz), nesnelerimizin veya Class'larımızın hangi özelliklere ve tiplere sahip olması gerektiğini belirten bir sözleşmedir. Bu, büyük ekiplerin tutarlı veri yapıları kullanmasını sağlar.
interface Urun {
id: number;
ad: string;
fiyat: number;
stoktaVarMi: boolean;
}
const telefon: Urun = {
id: 101,
ad: "Akıllı Telefon",
fiyat: 9999,
stoktaVarMi: true
}; // Eğer burada fiyatı unutsak, TS hemen uyarır.
4.6 Framework Temelleri: Büyük Yapıların Mimarı
Framework (Çerçeve) veya büyük kütüphaneler (React, Angular, Vue), sıfırdan her şeyi yazmak yerine, karmaşık uygulamaların hızlı ve düzenli bir şekilde geliştirilmesini sağlayan önceden hazırlanmış devasa araç kutularıdır. JS'te en çok kullanılanlar web arayüzü (Front-end) geliştirmeye odaklanır.
1. Neden Framework Kullanırız?
- Verimlilik: Tekrar eden görevleri (DOM manipülasyonu gibi) kendileri halleder, biz sadece iş mantığına odaklanırız.
- Yapı: Bize bir proje yapısı dayatarak, büyük ekiplerin tutarlı ve bakımı kolay kod yazmasını sağlar.
- State Management (Durum Yönetimi): Uygulamanın verilerinin (kullanıcının adı, sepetindeki ürünler) karmaşık hale gelmesini engeller.
2. Model-View-Controller (MVC) Mimarisi
Çoğu Framework, kodu düzenlemek için MVC veya benzeri bir mimari kullanır. Bu, sorumlulukları ayırır:
- Model: Uygulamanın verilerini ve iş mantığını yönetir (Veritabanından veri alma, hesaplama yapma).
- View (Görünüm): Kullanıcının gördüğü her şeyi yönetir (HTML, CSS). Veriyi Model'den alır.
- Controller (Kontrolcü): Kullanıcı etkileşimlerini (tıklamalar, form gönderme) yakalar ve Model ile View arasındaki iletişimi yönetir.
3. Popüler Framework'lerin Temel Kavramları
- React (Kütüphane): Component (Bileşen) bazlı çalışır. Her şey küçük, bağımsız HTML ve JS parçalarıdır (Buton, Navigasyon Çubuğu, Kart). Virtual DOM kullanarak performansı artırır.
- Vue.js (Framework): Öğrenmesi kolaydır. Reaktif Bağlama (Reactivity) konseptiyle, veri değiştiğinde View'in otomatik olarak güncellenmesini sağlar.
- Angular (Framework): En kapsamlısıdır. Tipik olarak TypeScript ile kullanılır ve hazır çözümlerin (Routing, Formlar) çoğunu içinde barındırır.
Framework'ler, profesyonel JavaScript geliştiricisinin olmazsa olmazıdır. Bu, sadece temel JS bilgisiyle yapabileceğiniz işlerin sınırlarını çok daha öteye taşır.
5.1 Node.js & Express.js: JavaScript ile Sunucu Programlama
Node.js, JavaScript'i tarayıcı dışına (sunucu tarafına, masaüstü uygulamalarına vb.) taşıyan, Chrome V8 motoru üzerine kurulmuş bir çalışma zamanı ortamıdır. Express.js ise Node.js üzerinde çalışan, web uygulamaları ve API'ler geliştirmeyi kolaylaştıran minimal ve esnek bir web çatısıdır (Framework).
1. Node.js'in Gücü ve Mimarisi
- Tek İş Parçacıklı ve Asenkron (Non-Blocking I/O): Tıpkı tarayıcı JS gibi, Node.js de Event Loop mimarisi üzerine kurulmuştur. Bu sayede, bir I/O (Input/Output - Dosya okuma, Veritabanı sorgulama) işlemi yapılırken beklemez, arka plana atar ve bu sırada diğer kullanıcı isteklerini işleyebilir. Bu, Node.js'i yüksek eşzamanlı (Concurrent) uygulamalar için çok verimli yapar.
- Kullanım Alanları: Gerçek zamanlı uygulamalar (chat), Akış Uygulamaları (Streaming), Hızlı ve Ölçeklenebilir API'ler.
- Temel Modüller: Node.js, dosya sistemi (`fs`), HTTP istekleri (`http`) ve yol (path) yönetimi gibi yerleşik modüllere sahiptir.
2. Express.js ile API Geliştirme
Express.js, Node.js'in karmaşık HTTP işlemlerini basitleştirir. Temel olarak Middleware ve Routing kavramlarına dayanır.
a. Routing (Yönlendirme):
Kullanıcıların hangi adrese (`/kullanicilar`, `/urunler/5`) hangi HTTP metoduyla (`GET`, `POST`, `PUT`, `DELETE`) istek yaptığına göre hangi fonksiyonun çalışacağını belirler.
const express = require('express');
const app = express();
// GET isteği: Tüm kullanıcıları listele
app.get('/api/kullanicilar', (req, res) => {
res.json([{ id: 1, ad: 'Ayşe' }]);
});
// POST isteği: Yeni bir kullanıcı oluştur
app.post('/api/kullanicilar', (req, res) => {
// req.body'den veriyi alır ve veritabanına kaydeder
res.status(201).send('Kullanıcı oluşturuldu');
});
app.listen(3000, () => console.log('Sunucu 3000 portunda çalışıyor.'));
b. Middleware (Ara Katman Yazılımı):
Bir istek (request) sunucuya ulaştığında, asıl işlevi yapacak fonksiyona gitmeden önce sırayla çalışan fonksiyonlardır. Tıpkı bir güvenlik kontrolü gibi, her istek önce bu aşamalardan geçer.
- Amaçları: Kullanıcının giriş yapmış olup olmadığını kontrol etme (Auth), gelen isteğin JSON formatını ayrıştırma, loglama yapma.
- Örnek: Gelen her isteği loglamak için:
app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] İstek geldi: ${req.method} ${req.url}`); next(); // Bir sonraki middleware veya route'a geç. });
5.2 WebSocket ve Gerçek Zamanlı Uygulamalar: İki Yönlü İletişim
Geleneksel HTTP, tek yönlüdür: Tarayıcı istek yapar, sunucu cevap verir ve bağlantı kapanır. Gerçek zamanlı uygulamalar (anlık sohbet, canlı spor skorları, borsa takibi) için bu yetersizdir. WebSocket, tarayıcı ve sunucu arasında kalıcı, çift yönlü (Full-Duplex) bir iletişim kanalı açar. Bu sayede sunucu, tarayıcının istek yapmasını beklemeden ona veri gönderebilir.
1. WebSocket Temelleri
- El Sıkışma (Handshake): WebSocket bağlantısı, standart HTTP üzerinden başlatılır (Upgrade Request). Başarılı olursa, protokol kalıcı olarak WebSocket'e yükseltilir.
- Düşük Gecikme (Low Latency): HTTP'deki gibi her iletişim için başlık (Header) gönderme ihtiyacı ortadan kalkar, bu da gecikmeyi ciddi ölçüde azaltır.
- Protokol: `ws://` (güvenli olmayan) ve `wss://` (güvenli, SSL/TLS) kullanır.
2. Socket.IO (Kullanımı Kolaylaştıran Kütüphane)
Saf WebSocket API'si biraz karmaşıktır. Bu nedenle çoğu uzman, bağlantı kesintilerini, yeniden bağlanmayı ve tarayıcı uyumluluğunu yönetmek için Socket.IO gibi kütüphaneleri kullanır.
a. Server Tarafı (Node.js/Socket.IO):
Sunucu, bir kullanıcı bağlandığında veya mesaj gönderdiğinde olayları dinler.
// Sunucu Tarafı (app.js)
const server = require('http').createServer();
const io = require('socket.io')(server);
io.on('connection', (socket) => {
console.log('Yeni bir kullanıcı bağlandı.');
// Mesaj dinle
socket.on('chatMessage', (msg) => {
// Mesajı tüm bağlı istemcilere geri yayınla
io.emit('chatMessage', msg);
});
});
server.listen(4000);
b. Client Tarafı (Tarayıcı JS):
Tarayıcı, sunucuya bağlanır ve olayları dinler/yayınlar.
// İstemci Tarafı (client.js)
const socket = io('http://localhost:4000');
// Sunucudan gelen mesajı dinle
socket.on('chatMessage', (msg) => {
console.log('Yeni mesaj:', msg);
});
// Sunucuya mesaj gönder
function gonder() {
socket.emit('chatMessage', 'Merhaba dünya!');
}
3. Alternatifler: Server-Sent Events (SSE)
WebSocket çift yönlü iken, SSE sadece sunucudan istemciye (tek yönlü) veri akışı için kullanılır. Canlı skorlar veya haber akışları gibi, istemcinin sunucuya cevap vermesine gerek olmayan durumlarda daha basit ve bazen daha verimli bir alternatiftir.
5.3 Package Management (NPM & Yarn): Bağımlılık Yönetimi
JavaScript ekosistemi, devasa bir açık kaynak kütüphane koleksiyonuna (paketlere) sahiptir. Package Manager (Paket Yöneticisi), bu kütüphaneleri projenize eklemeyi, kaldırmayı, güncellemeyi ve projenizin gereksinimlerini (bağımlılıklarını) yönetmeyi sağlar. NPM ve Yarn bu alandaki en popüler araçlardır.
1. NPM (Node Package Manager)
Node.js ile birlikte gelen varsayılan paket yöneticisidir ve dünyanın en büyük yazılım kayıt defterine sahiptir.
- `package.json`: Projenin kalbidir. Projenin adını, versiyonunu, çalıştırılacak komutları (`scripts`) ve en önemlisi, projenin tüm bağımlılıklarını (dependencies) listeler.
{ "name": "projem", "version": "1.0.0", "dependencies": { "express": "^4.17.1", // Üretim ortamında gerekli paketler "react": "^18.2.0" }, "devDependencies": { "jest": "^29.3.1" // Sadece geliştirme ve test aşamasında gerekli paketler } } - `node_modules`: Projeye kurulan tüm paketlerin ve onların kendi bağımlılıklarının tutulduğu ağır klasördür. Bu klasör, git gibi versiyon kontrol sistemlerine eklenmez.
- Yükleme Komutları:
- `npm install
`: Bağımlılığı indirir ve `package.json`'a ekler. - `npm start`: `package.json` içindeki `start` scriptini çalıştırır.
- `npm install
2. Yarn (Yeni Nesil Yönetici)
Facebook (Meta) tarafından NPM'in eksik yönlerini (özellikle hız ve güvenlik) gidermek için oluşturulmuştur. Artık NPM de Yarn ile rekabet edebilecek kadar gelişmiştir.
- Hız ve Paralel İşlem: Paketleri aynı anda (paralel olarak) indirerek NPM'den daha hızlı kurulum yapabilir.
- `yarn.lock` (Deterministik Kurulum): NPM'in `package-lock.json` dosyası gibi, `yarn.lock` dosyası da tam olarak hangi versiyonların yüklendiğini kaydeder. Bu, projenin herkesin bilgisayarında aynı şekilde çalışmasını garanti eder (Deterministic Install).
- Plug'n'Play (PnP): Yarn'ın modern versiyonlarında, paketleri `node_modules` klasörüne kopyalamadan yönetme seçeneği sunar, bu da kurulum süresini inanılmaz hızlandırır ve disk alanından tasarruf sağlar.
5.4 Babel ve Webpack: Modern Kodun Dönüşümü
Profesyonel JavaScript geliştirme, tek bir `app.js` dosyası yazmaktan ibaret değildir. Kodlarımızı optimize etmek, modülleri birleştirmek ve en yeni JS özelliklerini (ES6+) eski tarayıcılarda bile çalıştırmak için bu araçlara ihtiyacımız vardır. Bunlara Derleme Araçları (Build Tools) denir.
1. Babel (Transpiler)
Babel, bir Transpiler'dır. Modern (ES6, ES7, TypeScript) JavaScript kodunu alır ve onu eski, tüm tarayıcıların ve Node.js versiyonlarının anlayabileceği eski JS (ES5) koduna çevirir. Bu sayede en yeni özellikleri kullanırken uyumluluk sorunu yaşamayız.
Örnek: `const` ve Arrow Function kullanan kodu ES5'e çevirir:
// Babel Öncesi (ES6+)
const topla = (a, b) => a + b;
// Babel Sonrası (ES5)
var topla = function(a, b) {
return a + b;
};
- Presets: Belirli bir hedef ortam için (örneğin React veya son 2 tarayıcı versiyonu) gereken tüm dönüştürme kurallarını içeren hazır ayar paketleridir. (Örn: `babel-preset-env`)
2. Webpack (Module Bundler)
Webpack, bir Module Bundler (Modül Birleştirici)'dır. Projenizdeki yüzlerce JS, CSS, resim dosyasını alır ve onları tarayıcının kolayca yükleyebileceği optimize edilmiş, tek veya birkaç büyük dosyaya (Bundle) dönüştürür. Bu, sayfa yüklenme hızını artırır.
- Entry (Giriş): Webpack'in derlemeye başlayacağı ilk dosya (genellikle `index.js`).
- Output (Çıktı): Derlenmiş dosyaların nereye kaydedileceği (`dist` klasörü gibi).
- Loaders (Yükleyiciler): JS olmayan dosyaları (CSS, resimler) modüllere çevirir. Örneğin, `css-loader` ile CSS dosyaları JS içine aktarılabilir. Babel'in Webpack ile çalışmasını sağlayan da bir Loader'dır.
- Plugins (Eklentiler): Optimizasyon, sıkıştırma, ortam değişkenlerini tanımlama gibi daha karmaşık görevleri yaparlar.
- Code Splitting (Kod Bölme): Uygulamanın tamamını tek bir dosyada göndermek yerine, sadece o anki sayfa için gerekli kodu yükler. Bu, özellikle büyük uygulamaların ilk yüklenme süresini dramatik şekilde kısaltır.
5.5 Security & Deploy Süreçleri: Güvenlik ve Canlıya Alma
Yazdığınız kodun doğru çalışması kadar, güvenli olması ve kullanıcıya hatasız ulaştırılması da profesyonel bir geliştiricinin sorumluluğundadır. Bu kısım, bir projenin son aşamaları olan güvenlik ve dağıtım (Deployment) süreçlerine odaklanır.
1. Temel Web Güvenlik Kavramları (Front-end & Back-end)
JavaScript uygulamalarında en sık rastlanan ve önlenmesi gereken tehditler şunlardır:
- XSS (Cross-Site Scripting): Bir saldırganın, input alanları veya URL aracılığıyla sizin sitenizde kötü amaçlı JS kodu çalıştırmasıdır.
Önlem: Kullanıcıdan gelen tüm verileri DOM'a eklemeden önce temizlemek (`sanitize`) veya Text olarak eklemek. (React/Vue gibi Framework'ler bu konuda doğal koruma sağlar.) - CSRF (Cross-Site Request Forgery): Saldırganın, kullanıcının oturum açtığı bir sitede, kullanıcının haberi olmadan onun adına işlem yapmasını sağlayan bir istek göndermesidir.
Önlem: Express.js'te `csurf` gibi paketler kullanmak ve her forma rastgele bir CSRF token'ı eklemek. - Güvenli Veri Saklama: Hassas bilgileri (şifre, token) asla `localStorage`'da saklamayın. Şifreler her zaman hash'lenmiş olarak veritabanında tutulmalıdır. Oturum bilgileri için HTTP-Only cookie'ler tercih edilmelidir.
- CORS (Cross-Origin Resource Sharing): Farklı bir domain'den gelen isteklere sunucunun izin verip vermediğini kontrol etme mekanizması. API geliştirirken doğru CORS politikaları tanımlamak gerekir.
2. Deployment (Dağıtım) ve Süreçleri
Yazdığımız uygulamanın canlı (üretim) ortamda (Production Environment) kullanıcılar tarafından erişilebilir hale getirilmesi sürecidir.
- Build (Derleme) Aşaması: Canlıya almadan önce kodumuzu optimize ederiz. (Bkz: Webpack). Bu aşamada, kod sıkıştırılır (minify), gereksiz boşluklar silinir ve tüm modüller birleştirilir.
npm run build // Bu, genellikle kodun son halini bir "dist" klasörüne hazırlar. - Hosting (Barındırma): Projenin dosyalarının konulduğu sunucudur.
- Statik Hosting: Front-end projeleri (HTML, CSS, JS dosyaları) için (Netlify, Vercel, Firebase Hosting).
- Sunucu Hosting: Node.js/Express.js gibi Back-end projeleri için (AWS, Azure, DigitalOcean, Heroku).
- CI/CD (Continuous Integration/Continuous Deployment): Sürekli Entegrasyon ve Sürekli Dağıtım. Kodda yapılan her değişikliğin otomatik olarak test edildiği (CI) ve testleri geçerse otomatik olarak canlı ortama aktarıldığı (CD) süreçlerdir. Bu, manuel hataları ortadan kaldırır ve dağıtım hızını artırır (Örn: GitHub Actions, Jenkins).