Fonksiyonlar Derinlemesine
Giriş — Neden Fonksiyonlar?
Bir yemek tarifi düşünün. Her seferinde "Soğanları doğra, tavaya koy, kısık ateşte 5 dakika kavur..." adımlarını baştan yazmak yerine, "Soğan kavurması yap" dersiniz — ve herkes ne yapılacağını bilir. İşte fonksiyonlar da programlamada bunu yapar: tekrar eden işlemlere bir isim verip, ihtiyaç duyduğunuzda çağırmanızı sağlar.
Fonksiyonlar, programlamanın en temel yapı taşlarından biridir. Kod tekrarını önler, programı okunabilir kılar, test edilebilirliği artırır ve karmaşık sistemleri yönetilebilir parçalara böler. JavaScript'te fonksiyonlar birinci sınıf vatandaşlardır (first-class citizens) — bir değişkene atanabilir, başka fonksiyonlara parametre olarak geçirilebilir ve başka fonksiyonlardan döndürülebilir. Bu güçlü özellik, JavaScript'in fonksiyonel programlama paradigmasını desteklemesinin temelidir.
Analoji: Fonksiyonlar, bir fabrikanın makineleri gibidir. Hammadde (parametre) girer, işlenir ve ürün (return değeri) çıkar. Makineyi bir kez inşa edersiniz, sonra istediğiniz kadar hammadde vererek ürün üretirsiniz. Her seferinde makineyi yeniden inşa etmenize gerek yoktur.
Function Declaration (Fonksiyon Bildirimi)
En temel ve en eski fonksiyon tanımlama yöntemidir. function anahtar kelimesiyle başlar:
// Fonksiyon bildirimi
function selamla(isim) {
return `Merhaba, ${isim}!`;
}
// Fonksiyonu çağırma (invoke / call)
let mesaj = selamla("Ali");
console.log(mesaj); // "Merhaba, Ali!"
// Birden fazla parametre
function topla(a, b) {
return a + b;
}
console.log(topla(5, 3)); // 8
// Return olmadan — undefined döner
function logla(mesaj) {
console.log(mesaj);
// return yok → undefined döner
}
let sonuc = logla("Test"); // Konsola "Test" yazar
console.log(sonuc); // undefinedHoisting — Tanımdan Önce Çağırma
Function declaration'ların en önemli özelliği hoisting yapmasıdır — dosyanın başına "çekilir", yani tanımlanmadan önce çağrılabilir:
// ✅ Bu çalışır! Function declaration hoisting yapılır
console.log(kare(5)); // 25
function kare(sayi) {
return sayi * sayi;
}
// JavaScript bunu şöyle yorumlar:
// function kare(sayi) { return sayi * sayi; } ← en başa taşındı
// console.log(kare(5));Bu davranış ilk bakışta kullanışlı görünse de, kodun okunabilirliğini düşürebilir. Fonksiyonları kullandığınız yerin üstünde tanımlamak daha iyi bir pratiktir.
Function Expression (Fonksiyon İfadesi)
Bir fonksiyonu bir değişkene atayarak tanımlarsınız:
// Fonksiyon ifadesi — değişkene atanmış fonksiyon
const selamla = function(isim) {
return `Merhaba, ${isim}!`;
};
console.log(selamla("Ayşe")); // "Merhaba, Ayşe!"
// İsimli fonksiyon ifadesi (named function expression)
const faktoriyel = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // Kendi kendini çağırır (recursion)
};
console.log(faktoriyel(5)); // 120
// console.log(fact(5)); // ❌ ReferenceError — fact dışarıdan erişilemezDeclaration vs Expression — Farklar
// ❌ Function expression hoisting YAPMAZ
console.log(hesapla(5)); // ReferenceError: Cannot access 'hesapla' before initialization
const hesapla = function(x) {
return x * 2;
};
// const kullandığımız için TDZ (Temporal Dead Zone) kuralı geçerli| Özellik | Declaration | Expression |
|---|---|---|
| Hoisting | ✅ Tam hoisting | ❌ Hayır (TDZ) |
| Sözdizimi | function ad() {} | const ad = function() {} |
| İsimlendirme | Zorunlu | Opsiyonel |
| Kullanım yeri | Bağımsız ifade | Atama, parametre, return |
Arrow Functions (Ok Fonksiyonları) — ES6
Arrow function, daha kısa sözdizimi sunan modern fonksiyon tanımlama yöntemidir. ES6 ile gelmiştir ve günümüzde en çok tercih edilen yazım şeklidir:
// Standart fonksiyon ifadesi
const topla1 = function(a, b) {
return a + b;
};
// Arrow function — aynı işi daha kısa yapar
const topla2 = (a, b) => {
return a + b;
};
// Tek satırlık gövde — süslü parantez ve return gerekmez (implicit return)
const topla3 = (a, b) => a + b;
// Tek parametre — parantez de gerekmez
const kare = x => x * x;
// Parametresiz — boş parantez gerekir
const simdi = () => new Date();
// Nesne döndürme — parantez içine alın (yoksa gövde bloğu sanılır)
const kullaniciOlustur = (isim, yas) => ({ isim, yas });
console.log(kullaniciOlustur("Ali", 25)); // { isim: "Ali", yas: 25 }Arrow Function'ın Farklılıkları
Arrow function sadece kısa bir yazım değildir — bazı davranışları farklıdır:
// 1. Kendi `this`'i yoktur — dıştaki scope'un this'ini kullanır
const kullanici = {
isim: "Ali",
// Normal fonksiyon — kendi this'i var
selamNormal: function() {
console.log(`Merhaba, ${this.isim}`); // "Ali" — this → kullanici
},
// Arrow function — dışarıdaki this'i kullanır
selamArrow: () => {
console.log(`Merhaba, ${this.isim}`); // undefined — this → global/window
},
// Yaygın senaryo: iç içe callback'lerde arrow function avantajlı
bekle: function() {
// ❌ Normal fonksiyonla sorun
// setTimeout(function() {
// console.log(this.isim); // undefined — this kaybedildi!
// }, 1000);
// ✅ Arrow function ile çözüm
setTimeout(() => {
console.log(this.isim); // "Ali" — dıştaki this korunur
}, 1000);
}
};
// 2. arguments nesnesi yoktur
function normalFn() {
console.log(arguments); // [1, 2, 3] — çalışır
}
normalFn(1, 2, 3);
const arrowFn = () => {
// console.log(arguments); // ReferenceError
};
// Arrow'da rest parametreleri kullanın
const arrowFnFixed = (...args) => {
console.log(args); // [1, 2, 3]
};
arrowFnFixed(1, 2, 3);
// 3. Constructor olarak kullanılamaz
// const Kisi = (isim) => { this.isim = isim; };
// new Kisi("Ali"); // ❌ TypeError: Kisi is not a constructor⚠️ Dikkat: Nesne metotları tanımlarken arrow function kullanmayın — this beklediğiniz gibi çalışmaz. Nesne metotlarında normal fonksiyon veya shorthand syntax kullanın:
// ❌ Arrow function metot olarak
const hesap = {
bakiye: 1000,
paraCek: (miktar) => {
this.bakiye -= miktar; // this → global, hesap değil!
}
};
// ✅ Shorthand metot sözdizimi (önerilen)
const hesap2 = {
bakiye: 1000,
paraCek(miktar) {
this.bakiye -= miktar; // this → hesap2 ✅
}
};Ne Zaman Hangisini Kullanmalı?
// ✅ Arrow function kullanın:
// - Callback'lerde (map, filter, setTimeout, addEventListener)
const kareler = [1, 2, 3].map(x => x * x);
// - Kısa, tek amaçlı fonksiyonlarda
const ciftMi = n => n % 2 === 0;
// ✅ Normal function kullanın:
// - Nesne metotlarında
// - Constructor fonksiyonlarında
// - arguments nesnesine ihtiyaç duyulduğunda
// - Generator fonksiyonlarda (function*)Parametreler ve Argümanlar
Temel Parametreler
function selamla(isim, zaman) {
console.log(`İyi ${zaman}, ${isim}!`);
}
selamla("Ali", "akşamlar"); // "İyi akşamlar, Ali!"
selamla("Ali"); // "İyi undefined, Ali!" — zaman undefined
selamla("Ali", "sabahlar", "x"); // Fazla argüman görmezden gelinirDefault (Varsayılan) Parametreler — ES6
// ❌ Eski yöntem — || ile varsayılan
function selamlaEski(isim, zaman) {
isim = isim || "Misafir";
zaman = zaman || "günler";
console.log(`İyi ${zaman}, ${isim}!`);
}
// ✅ Modern yöntem — default parametreler
function selamla(isim = "Misafir", zaman = "günler") {
console.log(`İyi ${zaman}, ${isim}!`);
}
selamla(); // "İyi günler, Misafir!"
selamla("Ali"); // "İyi günler, Ali!"
selamla("Ali", "akşamlar"); // "İyi akşamlar, Ali!"
// Default değer bir ifade (expression) olabilir
function kayitOlustur(isim, tarih = new Date(), id = Math.random().toString(36).substr(2, 9)) {
return { isim, tarih, id };
}
// Default değer bir önceki parametreye bağlı olabilir
function dikdortgenAlan(en, boy = en) {
return en * boy;
}
console.log(dikdortgenAlan(5)); // 25 — kare (boy = en = 5)
console.log(dikdortgenAlan(5, 3)); // 15 — dikdörtgenRest Parametreleri (...) — ES6
Belirsiz sayıda argümanı bir diziye toplar:
// Rest parametresi — son parametre olmalı
function toplam(...sayilar) {
return sayilar.reduce((acc, n) => acc + n, 0);
}
console.log(toplam(1, 2, 3)); // 6
console.log(toplam(10, 20, 30, 40)); // 100
// Karma kullanım — ilk parametreler ayrı, geri kalanı rest
function takim(kaptan, ...oyuncular) {
console.log(`Kaptan: ${kaptan}`);
console.log(`Oyuncular: ${oyuncular.join(", ")}`);
console.log(`Toplam: ${oyuncular.length + 1} kişi`);
}
takim("Ali", "Ayşe", "Mehmet", "Fatma");
// Kaptan: Ali
// Oyuncular: Ayşe, Mehmet, Fatma
// Toplam: 4 kişiParametre Destructuring
Fonksiyon parametrelerinde doğrudan destructuring yapabilirsiniz — özellikle nesne parametrelerinde çok kullanışlıdır:
// ❌ Eski yöntem — sıra önemli, hangi parametre hangisi belli değil
function kullaniciOlusturEski(isim, yas, sehir, email, aktif) {
// 5 parametre — hangisi hangisi?
}
kullaniciOlusturEski("Ali", 25, "İstanbul", "ali@email.com", true);
// ✅ Nesne parametresi ile — sıra önemsiz, okunabilir
function kullaniciOlustur({ isim, yas, sehir = "Belirtilmedi", email, aktif = true }) {
return {
isim,
yas,
sehir,
email,
aktif,
kayitTarihi: new Date()
};
}
// Parametre sırası önemsiz, isimler açıklayıcı
let kullanici = kullaniciOlustur({
email: "ali@email.com",
isim: "Ali",
yas: 25
// sehir ve aktif varsayılan değerlerini kullanacak
});
console.log(kullanici);
// { isim: "Ali", yas: 25, sehir: "Belirtilmedi", email: "ali@email.com", aktif: true, kayitTarihi: ... }💡 İpucu: Bir fonksiyon 3'ten fazla parametre alıyorsa, tek bir nesne parametresi kullanın. Bu yaklaşım "named parameters" pattern'ı olarak bilinir ve büyük projelerde standart pratiktir.
Return — Değer Döndürme
// Tek değer döndürme
function kare(x) {
return x * x;
}
// return'den sonraki kod çalışmaz
function kontrol(yas) {
if (yas < 0) {
return "Geçersiz yaş"; // Fonksiyon burada sonlanır
}
// Geçerli yaş işlemleri...
return `Yaşınız: ${yas}`;
}
// Birden fazla değer döndürme — dizi veya nesne ile
function bolmeIslemi(bolunen, bolen) {
if (bolen === 0) {
return { basarili: false, hata: "Sıfıra bölme hatası" };
}
return {
basarili: true,
bolum: Math.floor(bolunen / bolen),
kalan: bolunen % bolen
};
}
// Destructuring ile alma
const { basarili, bolum, kalan } = bolmeIslemi(17, 5);
console.log(`17 / 5 = ${bolum}, kalan ${kalan}`); // "17 / 5 = 3, kalan 2"Early Return Pattern
Koşulları tersine çevirip erken döndürmek, kodun okunabilirliğini artırır:
// ❌ Derin iç içe yapı — "piramit of doom"
function siparisVer(kullanici, urun, miktar) {
if (kullanici) {
if (kullanici.aktif) {
if (urun) {
if (urun.stok >= miktar) {
// Asıl iş burada
return { basarili: true, mesaj: "Sipariş alındı" };
} else {
return { basarili: false, mesaj: "Yetersiz stok" };
}
} else {
return { basarili: false, mesaj: "Ürün bulunamadı" };
}
} else {
return { basarili: false, mesaj: "Hesap pasif" };
}
} else {
return { basarili: false, mesaj: "Kullanıcı bulunamadı" };
}
}
// ✅ Early return — düz ve okunabilir
function siparisVer2(kullanici, urun, miktar) {
if (!kullanici) {
return { basarili: false, mesaj: "Kullanıcı bulunamadı" };
}
if (!kullanici.aktif) {
return { basarili: false, mesaj: "Hesap pasif" };
}
if (!urun) {
return { basarili: false, mesaj: "Ürün bulunamadı" };
}
if (urun.stok < miktar) {
return { basarili: false, mesaj: "Yetersiz stok" };
}
// Tüm kontrollerden geçti — asıl iş
return { basarili: true, mesaj: "Sipariş alındı" };
}Callback Fonksiyonlar
JavaScript'te fonksiyonlar birinci sınıf vatandaşlardır — başka fonksiyonlara argüman olarak geçirilebilir. Bu şekilde geçirilen fonksiyonlara callback denir:
// Basit callback
function islemYap(sayi, callback) {
let sonuc = callback(sayi);
console.log(`Sonuç: ${sonuc}`);
}
islemYap(5, x => x * 2); // Sonuç: 10
islemYap(5, x => x * x); // Sonuç: 25
islemYap(5, x => x + 100); // Sonuç: 105
// Gerçek dünya — dizi metotlarında callback
const sayilar = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Her elemanı 2 ile çarp
const ikiKati = sayilar.map(n => n * 2);
console.log(ikiKati); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// Çift olanları filtrele
const ciftler = sayilar.filter(n => n % 2 === 0);
console.log(ciftler); // [2, 4, 6, 8, 10]
// Toplamını bul
const toplam = sayilar.reduce((acc, n) => acc + n, 0);
console.log(toplam); // 55Higher-Order Functions (Yüksek Dereceli Fonksiyonlar)
Bir fonksiyon parametre olarak fonksiyon alıyorsa veya fonksiyon döndürüyorsa, buna higher-order function denir:
// Fonksiyon döndüren fonksiyon
function carpanOlustur(carpan) {
return function(sayi) {
return sayi * carpan;
};
}
const ikiKatina = carpanOlustur(2);
const ucKatina = carpanOlustur(3);
console.log(ikiKatina(5)); // 10
console.log(ucKatina(5)); // 15
// Arrow function ile daha kısa
const carpanOlustur2 = carpan => sayi => sayi * carpan;
// Gerçek dünya — validasyon fonksiyonu oluşturucu
function minUzunlukKontrolu(minUzunluk) {
return function(deger) {
return deger.length >= minUzunluk;
};
}
const sifreKontrol = minUzunlukKontrolu(8);
const kullaniciAdiKontrol = minUzunlukKontrolu(3);
console.log(sifreKontrol("abc123")); // false (6 < 8)
console.log(sifreKontrol("abc12345")); // true (8 >= 8)
console.log(kullaniciAdiKontrol("Al")); // false (2 < 3)
console.log(kullaniciAdiKontrol("Ali")); // true (3 >= 3)IIFE — Anında Çalıştırılan Fonksiyon
IIFE (Immediately Invoked Function Expression), tanımlandığı anda çalıştırılan fonksiyondur:
// IIFE — parantezle sarılır ve hemen çağrılır
(function() {
let gizliDeger = 42;
console.log("IIFE çalıştı!", gizliDeger);
})();
// gizliDeger dışarıdan erişilemez — kapsülleme sağlar
// Arrow function ile IIFE
(() => {
console.log("Arrow IIFE!");
})();
// Parametre alan IIFE
((isim) => {
console.log(`Merhaba, ${isim}!`);
})("Ali");
// Gerçek dünya kullanımı — modül pattern (ES6 modules öncesi)
const sayacModulu = (() => {
let sayac = 0; // Özel (private) değişken
return {
artir: () => ++sayac,
azalt: () => --sayac,
deger: () => sayac
};
})();
sayacModulu.artir();
sayacModulu.artir();
sayacModulu.artir();
console.log(sayacModulu.deger()); // 3
// console.log(sayac); // ❌ ReferenceError — dışarıdan erişilemez⚠️ Dikkat: IIFE, ES6 modül sistemi öncesinde çok yaygındı. Modern JavaScript'te import/export modül sistemi sayesinde IIFE'ye daha az ihtiyaç duyulur. Ama eski kodlarda sıkça karşılaşırsınız ve kapsülleme (encapsulation) kavramını anlamak için bilmek önemlidir.
Gerçek Dünya Örneği: Form Validasyon Sistemi
// Yeniden kullanılabilir validasyon sistemi
// Higher-order functions ve callback'lerle tasarlanmış
// Validasyon kuralları oluşturucular
const zorunlu = (alanAdi) => (deger) =>
deger && deger.trim() !== ""
? { gecerli: true }
: { gecerli: false, hata: `${alanAdi} zorunludur` };
const minUzunluk = (alanAdi, min) => (deger) =>
deger && deger.length >= min
? { gecerli: true }
: { gecerli: false, hata: `${alanAdi} en az ${min} karakter olmalı` };
const emailFormati = (alanAdi) => (deger) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(deger)
? { gecerli: true }
: { gecerli: false, hata: `${alanAdi} geçerli bir e-posta olmalı` };
const sayiAraligi = (alanAdi, min, max) => (deger) => {
const sayi = Number(deger);
if (isNaN(sayi)) return { gecerli: false, hata: `${alanAdi} bir sayı olmalı` };
if (sayi < min || sayi > max) return { gecerli: false, hata: `${alanAdi} ${min}-${max} arasında olmalı` };
return { gecerli: true };
};
// Validasyon çalıştırıcı
function formDogrula(veri, kurallar) {
const hatalar = [];
for (const [alan, kontrolListesi] of Object.entries(kurallar)) {
for (const kontrol of kontrolListesi) {
const sonuc = kontrol(veri[alan]);
if (!sonuc.gecerli) {
hatalar.push(sonuc.hata);
break; // İlk hatada dur, diğer kontrollere geçme
}
}
}
return {
gecerli: hatalar.length === 0,
hatalar
};
}
// Kullanım
const formVerisi = {
isim: "Al",
email: "ali-yanlis-format",
yas: "150",
sifre: "123"
};
const kurallar = {
isim: [zorunlu("İsim"), minUzunluk("İsim", 3)],
email: [zorunlu("E-posta"), emailFormati("E-posta")],
yas: [zorunlu("Yaş"), sayiAraligi("Yaş", 1, 120)],
sifre: [zorunlu("Şifre"), minUzunluk("Şifre", 8)]
};
const sonuc = formDogrula(formVerisi, kurallar);
if (sonuc.gecerli) {
console.log("✅ Form geçerli!");
} else {
console.log("❌ Form hataları:");
sonuc.hatalar.forEach((hata, i) => {
console.log(` ${i + 1}. ${hata}`);
});
}
// ❌ Form hataları:
// 1. İsim en az 3 karakter olmalı
// 2. E-posta geçerli bir e-posta olmalı
// 3. Yaş 1-120 arasında olmalı
// 4. Şifre en az 8 karakter olmalıBu örnekte higher-order functions, closure, arrow functions, destructuring ve callback pattern'ları gerçek bir senaryoda bir arada kullanıldı.
Saf (Pure) Fonksiyonlar
Saf fonksiyonlar, aynı girdiye her zaman aynı çıktıyı veren ve yan etki (side effect) üretmeyen fonksiyonlardır. Fonksiyonel programlamanın temel taşıdır:
// ✅ SAF fonksiyon — aynı girdi = aynı çıktı, yan etki yok
function topla(a, b) {
return a + b;
}
// ❌ SAF DEĞİL — dış değişkene bağlı (yan etki)
let vergiOrani = 0.18;
function vergiHesapla(fiyat) {
return fiyat * vergiOrani; // Dış değişkene bağlı!
}
// ✅ SAF hale getirme — tüm bağımlılıklar parametre olarak
function vergiHesaplaSaf(fiyat, oran) {
return fiyat * oran;
}
// ❌ SAF DEĞİL — diziyi değiştiriyor (mutation)
function elemanEkle(dizi, eleman) {
dizi.push(eleman); // Orijinal diziyi değiştiriyor!
return dizi;
}
// ✅ SAF — yeni dizi döndürüyor
function elemanEkleSaf(dizi, eleman) {
return [...dizi, eleman]; // Yeni dizi oluşturur
}💡 İpucu: Mümkün olduğunca saf fonksiyonlar yazın. Test etmesi, debug etmesi ve mantığını anlaması çok daha kolaydır. Yan etkiler (DOM değiştirme, API çağrısı, dosya yazma) kaçınılmazdır ama bunları izole edin ve programın geri kalanını saf tutun.
Özet
🔹 Function declaration hoisting yapılır (tanımdan önce çağrılabilir), function expression yapılmaz
🔹 Arrow function kısa sözdizimi sunar ama
thisbağlamı farklıdır — callback'lerde idealdir, nesne metotlarında kullanmayın🔹 Default parametreler (
= değer) ile varsayılan değerler atanabilir, 3+ parametre varsa nesne parametresi kullanın🔹 Rest parametreleri (
...args) belirsiz sayıda argümanı diziye toplar, destructuring parametrelerde karmaşık yapıları çözümler🔹 Higher-order functions fonksiyon alır veya döndürür —
map,filter,reducebunların en yaygın örnekleridir🔹 Early return pattern'ı iç içe if yapılarını düzleştirir — önce hata durumlarını kontrol edip erken dönün
AI Asistan
Sorularını yanıtlamaya hazır