Mert Özen Her Satırda Daha İleri
Frontend JavaScript Temel — #11

JavaScript Fonksiyonları: Declaration, Arrow Function, Parametreler ve return

Mert Özen 3 Kas 2026 8 dk 5 görüntülenme
JavaScript Fonksiyonları: Declaration, Arrow Function, Parametreler ve return

Function declaration, expression ve arrow function farkını, parametre ve argümanı, varsayılan parametreleri, return'ü, callback olarak fonksiyon vermeyi, fonksiyon scope'unu ve iyi fonksiyon yazmayı öğreniyoruz.

Sürüm notu: Bu yazıdaki kodlar ES2026 ve güncel tarayıcılar (Chrome, Edge, Firefox, Safari) içindir.

Önce Tahmin Et

Serinin başından beri console.log, Math.round, sort gibi hazır fonksiyonları kullanıyoruz. Bu yazıda kendi fonksiyonlarımızı yazacağız. Önce şu küçük koda bak:

function greet(name) {
  console.log(`Hello ${name}`);
}

const result = greet();
console.log(result);

Konsolda kaç satır çıkar ve bu satırlarda ne yazar? Bir hata mı alırsın, yoksa kod sessizce çalışıp bir şeyler mi yazdırır? Okumaya devam etmeden önce tahminini yap. Bu küçük kod aslında iki ayrı soru soruyor; ikisini de parametreler ve return bölümlerinde cevaplayacağız.

Fonksiyon Nedir, Nasıl Tanımlanır?

Bir fonksiyonu bir kahve makinesi gibi düşün. İçine bir şeyler koyarsın (çekirdek, su), makine kendi bildiği işi yapar ve sana bir sonuç verir (kahve). Makinenin içinde ne olduğunu her seferinde bilmen gerekmez; düğmeye basman yeter. Fonksiyon da bir kez yazılıp istediğin kadar çalıştırılabilen, isimlendirilmiş bir kod parçasıdır.

En klasik tanımlama biçimi function declaration. Bunu tarayıcının konsoluna yapıştırıp dene:

function sayHello() {
  console.log("Hello!");
}

sayHello(); // Hello!
sayHello(); // Hello!

function anahtar kelimesi, bir isim, parantez ve süslü parantez içinde gövde. Tanımlamak fonksiyonu çalıştırmaz; yalnızca makineyi tezgâha koyar. Çalıştırmak, yani çağırmak için adının sonuna parantez koyarsın. Parantezsiz sayHello makinenin kendisi, sayHello() ise düğmeye basmak.

Function declaration'ın küçük bir ayrıcalığı var: onu, tanımladığın satırdan önce de çağırabilirsin. Motor kodu çalıştırmadan önce bu tanımları bir tur tarar ve hazır eder. Bu davranışın adı hoisting; nasıl işlediğini Orta seride ayrıntısıyla göreceğiz.

Parametre ve Argüman

Fonksiyonun dışarıdan değer alabilmesi için parantezin içine parametre yazarsın. Parametre, fonksiyonun içinde kullanılan bir yer tutucudur. Çağırırken verdiğin gerçek değere ise argüman denir:

function greet(name, city) {
  console.log(`Hello ${name}, how is ${city}?`);
}

greet("Ada", "Izmir");         // Hello Ada, how is Izmir?
greet("Grace");                   // Hello Grace, how is undefined?
greet("Alan", "Bursa", "extra"); // Hello Alan, how is Bursa?

Burada name ve city parametre, "Ada" ve "Izmir" argüman. JavaScript argüman sayısı konusunda hiç titiz değildir. Eksik verirsen hata vermez, karşılığı olmayan parametre undefined olur. Fazla verirsen fazlalığı görmezden gelir.

Baştaki sorunun ilk yarısı buradan çözülüyor. greet()'i hiç argüman vermeden çağırdık, bu yüzden name undefined oldu ve ilk satırda Hello undefined yazdı.

Bunu engellemenin temiz yolu ES2015 ile gelen varsayılan parametreler. Parametrenin yanına bir eşittir işaretiyle varsayılan değer yazarsın; argüman verilmezse o kullanılır:

function greet(name = "guest") {
  console.log(`Hello ${name}`);
}

greet("Ada"); // Hello Ada
greet();       // Hello guest

return: Fonksiyondan Sonuç Almak

Şimdiye kadarki fonksiyonlar bir şey yaptı ama bize bir şey vermedi. Bir hesap yapıp sonucunu geri almak istiyorsan return kullanırsın:

function calculateTotal(price, quantity) {
  return price * quantity;
}

const total = calculateTotal(150, 3);
console.log(total);                     // 450
console.log(calculateTotal(20, 2) + 5); // 45

return iki iş yapar: arkasındaki değeri fonksiyonun çağrıldığı yere geri gönderir ve fonksiyonu orada bitirir. return'den sonraki satırlar çalışmaz. Kahve makinesi örneğinde fincanı sana uzattığı an.

Baştaki sorunun ikinci yarısı da burada. greet içinde console.log var ama return yok. return olmayan bir fonksiyon her zaman undefined döner. Yani result değişkenine undefined atandı ve ikinci satırda o yazdı. Konsola yazdırmak ile değer döndürmek aynı şey değil. console.log sonucu sana gösterir; return ise sonucu kodun geri kalanına teslim eder. Bu arada konsolda en altta soluk renkli bir undefined daha görürsen şaşırma; o da son satırdaki console.log'un kendi dönüş değeri, çünkü onun da döndürdüğü bir şey yok.

Daha önce koşullar yazısında işaret ettiğimiz erken return fikri de burada işe yarıyor. Geçersiz durumu en başta eleyip fonksiyondan çıkarsan, geri kalan kod iç içe if'lere boğulmaz:

function getDiscount(total) {
  if (total < 100) {
    return 0;
  }
  if (total < 500) {
    return total * 0.05;
  }
  return total * 0.1;
}

console.log(getDiscount(80));   // 0
console.log(getDiscount(200));  // 10
console.log(getDiscount(1000)); // 100

Function Expression ve Arrow Function

Bir fonksiyonu bir değişkene atayarak da tanımlayabilirsin. Buna function expression denir:

const square = function (number) {
  return number * number;
};

console.log(square(4)); // 16

Declaration'dan farkı, bir değişkenin içinde yaşaması. const ile tanımlandığı için, daha önce değişkenler yazısında gördüğümüz TDZ kuralı geçerli: tanımlandığı satırdan önce çağırırsan ReferenceError alırsın.

ES2015 ile gelen arrow function ise aynı fikri daha kısa yazmanın yolu. function kelimesi gider, parametrelerle gövdenin arasına bir ok (=>) gelir:

const square = (number) => {
  return number * number;
};

const double = number => number * 2;
const add = (a, b) => a + b;
const getRandom = () => Math.random();

console.log(square(4)); // 16
console.log(double(5)); // 10
console.log(add(2, 3)); // 5

Kısaltma kuralları basit. Tek parametre varsa parantezi atabilirsin; hiç parametre yoksa ya da birden fazlaysa parantez şart. Gövde tek bir ifadeden oluşuyorsa süslü parantezi ve return'ü atabilirsin; o ifadenin sonucu kendiliğinden döner. Gövdeyi süslü paranteze aldığın anda ise return'ü kendin yazman gerekir.

Hangisini kullanacaksın? Modern kodda kısa, tek satırlık işler ve callback'ler için arrow function çok yaygın. Sayfanın ana işlerini yapan, isimli fonksiyonlarda declaration da hâlâ tercih ediliyor. Arrow function'ın this konusunda da farklı davrandığını Orta seride göreceğiz; şimdilik ikisini de rahatça kullanabilirsin.

Fonksiyonlar Birer Değerdir

JavaScript'te fonksiyon, sayı ya da string gibi bir değerdir. Bir değişkene atayabilir, bir array'in içine koyabilir, başka bir fonksiyona argüman olarak verebilirsin. Bir önceki yazıda forEach ve sort'a fonksiyon verirken tam olarak bunu yaptık. Başka bir fonksiyona verilen ve onun tarafından uygun anda çağrılan bu fonksiyonlara callback denir:

function calculate(a, b, operation) {
  return operation(a, b);
}

const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

console.log(calculate(6, 3, add));             // 9
console.log(calculate(6, 3, multiply));        // 18
console.log(calculate(6, 3, (a, b) => a - b)); // 3

calculate ne tür bir işlem yapacağını bilmiyor; işlemi ona sen veriyorsun. Kahve makinesine kapsül takmak gibi: makine aynı, kapsül değişince çıkan şey değişiyor. Son satırda fonksiyonu bir değişkene bile koymadan, doğrudan argüman olarak yazdık. Bu kalıp event listener'lardan zamanlayıcılara kadar tarayıcı tarafında sürekli karşına çıkacak.

Fonksiyonun Kendi Alanı: Scope

Fonksiyonun içinde tanımladığın değişkenler o fonksiyona aittir; dışarıdan görünmez. Fonksiyonu tek yönlü camı olan bir oda gibi düşün. İçeriden dışarıyı görebilirsin, dışarıdan içeriyi göremezsin:

const taxRate = 0.2;

function addTax(price) {
  const tax = price * taxRate;
  return price + tax;
}

console.log(addTax(100)); // 120
console.log(typeof tax);  // "undefined"

addTax dışarıdaki taxRate'i okuyabildi. Ama içeride oluşturduğu tax dışarıya sızmadı; fonksiyon bittiğinde o değişkenin işi de bitti. Parametreler de aynı kurala uyar, onlar da fonksiyonun yerel değişkenleridir. Bu, daha önce gördüğümüz block scope'un fonksiyon versiyonu. Scope zincirini ve closure'ı Orta seride derinlemesine ele alacağız.

Bu kural bir güvenlik ağıdır. Her fonksiyonun kendi değişkenleri olduğu için, iki farklı fonksiyonda total adında değişken kullanmak birbirini bozmaz.

İyi Fonksiyon Yazmak

Fonksiyon yazmak kolay; okunması, test edilmesi ve değiştirilmesi kolay fonksiyon yazmak bir alışkanlık meselesi. Birkaç kural seni uzun yol götürür.

Bir fonksiyon tek bir iş yapsın. Adını koyarken "ve" kelimesine ihtiyaç duyuyorsan, muhtemelen iki fonksiyon yazman gerekiyordur. validateAndSaveAndNotify yerine üç ayrı fonksiyon her zaman daha kolay yönetilir.

İsim bir fiille başlasın ve ne yaptığını söylesin: calculateTotal, formatPrice, getUserName. true ya da false dönen fonksiyonlar için is, has, can ile başlamak güzel bir gelenek: isValidEmail, hasDiscount. Kodu okuyan kişi fonksiyonun içine bakmadan ne yaptığını anlayabilmeli.

Parametre sayısını az tut. Üçü geçtiyse çağıran kişi hangi sıranın ne olduğunu karıştırmaya başlar. Ve mümkün olduğunca dışarıdaki değişkenleri değiştirmek yerine bir değer döndür. Aynı girdiye her zaman aynı çıktıyı veren bir fonksiyonun davranışını tahmin etmek çok daha kolaydır.

Sık Düşülen Tuzaklar

Fonksiyonu vermek yerine çağırmak

const showItem = (item) => console.log(`Item: ${item}`);

["pen", "notebook"].forEach(showItem);    // doğru: fonksiyonu veriyoruz
// ["pen", "notebook"].forEach(showItem()); // yanlış: önce çağırıp sonucunu veriyoruz

Callback verirken parantez koyarsan fonksiyon hemen çalışır ve forEach'e onun dönüş değeri gider; burada undefined. Sonuç bir TypeError. Kural basit: makineyi teslim ediyorsan parantezsiz, düğmeye basıyorsan parantezli.

Süslü parantezli arrow'da unutulan return

const double = (n) => { n * 2 };
console.log(double(5)); // undefined

const makeUser = (name) => ({ name: name, active: true });
console.log(makeUser("Ada")); // { name: "Ada", active: true }

Süslü parantezi açtığın an örtük return kaybolur; ya parantezi kaldır ya da return yaz. İkinci satırdaki durum da bunun akrabası. Arrow function'dan doğrudan bir object döndürmek istiyorsan object'i normal paranteze almalısın, yoksa motor süslü parantezi fonksiyon gövdesi sanar. Object'leri bir sonraki yazıda detaylandıracağız.

Varsayılan değer null'da devreye girmez

function greet(name = "guest") {
  return `Hello ${name}`;
}

console.log(greet(undefined)); // "Hello guest"
console.log(greet(null));      // "Hello null"

Varsayılan parametre yalnızca argüman undefined olduğunda kullanılır. null, boş string ya da 0 geçerli değerler sayılır ve olduğu gibi içeri girer.

Küçük Bir Deneme

★☆☆ İki sayı alıp büyük olanı döndüren bir getMax fonksiyonu yaz; önce function declaration, sonra tek satırlık bir arrow function olarak. İkisinin de aynı sonucu verdiğini konsolda gör.

★★☆ Çalıştırmadan önce tahmin et: const f = (x = 10) => x * 2; için f(), f(3), f(null) ve f("4") ne döner? Son ikisinde, daha önce operatörler yazısında gördüğümüz coercion'ı hatırla.

★★☆ Bir fiyatı alıp "₺1.234,50" biçiminde döndüren bir formatPrice fonksiyonu yaz. Daha önce gördüğümüz Intl.NumberFormat burada işine yarayacak. Sonra bir fiyat array'indeki her değeri forEach ile bu fonksiyondan geçirip yazdır.

★★★ repeat(times, action) adında bir fonksiyon yaz: action fonksiyonunu times kez çağırsın ve her seferinde kaçıncı tur olduğunu argüman olarak versin. repeat(3, (round) => console.log(`Round ${round}`)) çağrısı üç satır yazdırmalı.

Fonksiyonlarla kodu küçük, isimli parçalara bölmeyi öğrendik. Ama gerçek dünyadaki veriler çoğu zaman tek bir sayı ya da string değil: bir kullanıcının adı, e-postası, yaşı birlikte gelir. Bir sonraki yazıda bu tür ilişkili verileri bir arada tutan object'lere geçiyoruz.