JavaScript'te bir fonksiyonu üç yaygın yolla yazarsınız: function declaration (fonksiyon bildirimi), function expression (fonksiyon ifadesi) ve arrow function (ok fonksiyonu). Üçü de çağrılabilir bir fonksiyon üretir, ama davranışları aynı değildir. Bildirimler hoisting sayesinde tanımlanmadan önce çağrılabilir; ifadeler ve arrow function'lar çağrılamaz. Arrow function'ların kendine ait this ve arguments bağlaması yoktur ve new ile kurucu olarak kullanılamazlar.

Bu yazıda her yazım şeklini kısa ve çalıştırılabilir örneklerle anlatıyoruz, sonra farkları bir tabloda topluyoruz. Örneklerin hepsi Node.js 24 ile çalıştırıldı ve yorum satırlarındaki çıktılar gerçek çıktılardır. this konusunun tamamı için this anahtar kelimesi rehberine, parametre ayrıntıları için default ve rest parametreler yazısına bakabilirsiniz.

Fonksiyon tanımlamanın üç yolu

Fonksiyon tanımlamanın üç yolu

MDN, JavaScript'te fonksiyonların birinci sınıf nesne (first-class object) olduğunu vurgular: Bir fonksiyonu değişkene atayabilir, başka bir fonksiyona argüman olarak verebilir ya da bir fonksiyondan döndürebilirsiniz. Onu diğer nesnelerden ayıran tek şey çağrılabilmesidir. Aşağıda aynı işi yapan üç yazım şekli var:

javascript
function kare(x) {
  return x * x;
}
const kup = function (x) {
  return x * x * x;
};
const ikiKat = (x) => x * 2;

console.log(kare(3), kup(2), ikiKat(5)); // 9 8 10
console.log(typeof kare, typeof kup, typeof ikiKat); // function function function

Üçü için de typeof sonucu "function" olur. Farklar, fonksiyonun ne zaman kullanılabilir hâle geldiğinde, this değerinin nereden geldiğinde ve kurucu olarak kullanılıp kullanılamayacağında ortaya çıkar. Bunların yanında MDN, nesne içindeki kısa metot yazımını ({ metot() {} }) ve new Function() kurucusunu da sayar; kurucu, metni çalışma anında derlediği ve yalnızca global kapsama eriştiği için genellikle önerilmez.

Function declaration: hoisting ile önceden kullanılabilir

Bir satır function anahtar kelimesiyle başlıyor ve fonksiyona ad veriyorsa bu bir bildirimdir. Bildirimin en belirgin özelliği hoisting'dir: Fonksiyon, değeriyle birlikte içinde bulunduğu kapsamın en üstüne taşınmış gibi davranır. Böylece dosyada daha aşağıda tanımladığınız bir fonksiyonu yukarıda çağırabilirsiniz. Aynı şey var ile atanan bir fonksiyon ifadesinde işe yaramaz:

javascript
console.log(topla(2, 3)); // 5

function topla(a, b) {
  return a + b;
}

try {
  carp(2, 3);
} catch (e) {
  console.log(e.name + ": " + e.message); // TypeError: carp is not a function
}

var carp = function (a, b) {
  return a * b;
};

var carp bildirimi de yukarı taşınır, ama yalnızca adı taşınır, değeri undefined olarak kalır. Bu yüzden hata ReferenceError değil TypeError olur: undefined bir değeri çağırmaya çalışıyorsunuz. let ve const ile durum farklıdır; ayrıntıları hoisting ve TDZ yazısında anlatıyoruz.

Function expression: değer olarak fonksiyon

Fonksiyon ifadesi, function anahtar kelimesinin bir ifade konumunda kullanılmasıdır: bir değişkene atama, bir argüman ya da bir nesne özelliği. En önemli farkı adın isteğe bağlı olmasıdır. Adı verirseniz bu ad yalnızca fonksiyonun kendi gövdesinden görünür; özyinelemeli (recursive) fonksiyonlar için kullanışlıdır:

javascript
const faktoriyel = function fakt(n) {
  return n <= 1 ? 1 : n * fakt(n - 1);
};

console.log(faktoriyel(5)); // 120
console.log(faktoriyel.name); // fakt
console.log(typeof fakt); // undefined

const anonim = function () {};
console.log(anonim.name); // anonim

Son satıra dikkat edin: Adsız bir fonksiyon ifadesi bir değişkene atandığında motor, name özelliğini değişken adından çıkarır. Adlı ifadede ise name, verdiğiniz addır ve bu ad dışarıdan erişilemez. Fonksiyon ifadesinin bir diğer yaygın kullanımı IIFE'dir (Immediately Invoked Function Expression), yani tanımlandığı anda çalışan fonksiyon. Bir ifade satırı function ile başlayamadığı için IIFE parantez içine alınır: (function () { ... })();. Modüller ve blok kapsamlı let/const geldikten sonra IIFE ihtiyacı azaldı; neden kullanıldığını scope yazısında görebilirsiniz.

Arrow function: kısa yazım ve kurallar

Arrow function: kısa yazım ve kurallar: x => x * 2, (a, b) => a + b, () => { ... return sonuc; }, async (url) => { ... }

Arrow function, geleneksel fonksiyon ifadesine kısa bir alternatif olan, ama bazı bilinçli kısıtlamaları olan yazımdır. Tek bir basit parametre varsa parantezler, gövde tek bir ifadeyse süslü parantezler ve return atlanabilir; bu durumda ifadenin değeri otomatik olarak döner (implicit return). Parametre yoksa, birden fazla parametre varsa ya da default, rest veya destructuring kullanıyorsanız parantez zorunludur.

  • x => x * 2: tek parametre, ifade gövdesi, otomatik dönüş
  • (a, b) => a + b: birden fazla parametre, parantez zorunlu
  • () => { ... return sonuc; }: blok gövdesi, return açıkça yazılmalı
  • async (url) => { ... }: başına async eklenerek asenkron hâle getirilebilir

En sık yapılan hata, nesne döndürmeye çalışırken süslü parantezi doğrudan yazmaktır. Motor, okun hemen ardından gelen { karakterini blok gövdesinin başlangıcı sayar ve içindeki ad: kısmını bir etiket (label) olarak yorumlar:

javascript
const yanlis = () => { ad: "Ali" };
const dogru = () => ({ ad: "Ali" });

console.log(yanlis()); // undefined
console.log(dogru()); // { ad: 'Ali' }

Fark 1: this değeri

Normal bir fonksiyonda this, fonksiyonun nasıl çağrıldığına göre belirlenir. Arrow function'ın ise kendi this bağlaması yoktur; tanımlandığı yerdeki this değerini kullanır. Bu fark, bir metodun içinde callback yazarken hemen görünür:

javascript
"use strict";

const sepet = {
  sahip: "Ayşe",
  urunler: ["kalem", "defter"],
  listeleOk() {
    return this.urunler.map((u) => `${this.sahip}: ${u}`);
  },
  listeleNormal() {
    return this.urunler.map(function (u) {
      return `${this === undefined ? "this yok" : this.sahip}: ${u}`;
    });
  },
};

console.log(sepet.listeleOk()); // [ 'Ayşe: kalem', 'Ayşe: defter' ]
console.log(sepet.listeleNormal()); // [ 'this yok: kalem', 'this yok: defter' ]

map callback'i herhangi bir nesneye bağlı olmadan çağırır; strict mode'da bu, normal fonksiyonun içinde this değerinin undefined olması demektir. Arrow function ise listeleOk metodunun this değerini, yani sepet nesnesini kullanır. Aynı nedenle arrow function'ı nesne metodu olarak yazmayın: Metodun içindeki this nesneyi değil, dışarıdaki kapsamı gösterir. call(), apply() ve bind() da arrow function'ın this değerini değiştiremez.

Fark 2: arguments nesnesi

Arrow olmayan her fonksiyonun içinde, çağrıda gelen tüm argümanları tutan dizi benzeri bir arguments nesnesi bulunur. Arrow function'da bu nesne yoktur; arguments yazarsanız çevreleyen normal fonksiyonun arguments nesnesine ulaşırsınız. Arrow function'da değişken sayıda argüman için rest parametresi kullanılır:

javascript
function normal() {
  return arguments.length;
}
const ok = (...args) => args.length;

console.log(normal(1, 2, 3), ok(1, 2, 3)); // 3 3

function dis() {
  const ic = () => arguments[0];
  return ic("iç değer");
}
console.log(dis("dış değer")); // dış değer

Fark 3: new ile kullanım

Fonksiyon bildirimi ve fonksiyon ifadesi ile oluşturulan normal fonksiyonlar new ile kurucu olarak çağrılabilir. Arrow function'lar ve nesne içindeki kısa metot yazımı ise kurucu olamaz; prototype özellikleri de yoktur. MDN ayrıca async fonksiyonların ve generator'ların hangi yazımla tanımlanırsa tanımlansın kurucu olarak kullanılamadığını belirtir.

javascript
function Kisi(ad) {
  this.ad = ad;
}
const k = new Kisi("Ali");
console.log(k.ad); // Ali

const OkKisi = (ad) => {
  this.ad = ad;
};
try {
  new OkKisi("Veli");
} catch (e) {
  console.log(e.name + ": " + e.message); // TypeError: OkKisi is not a constructor
}
console.log("prototype" in Kisi, "prototype" in OkKisi); // true false

const nesne = { metot() {} };
try {
  new nesne.metot();
} catch (e) {
  console.log(e.message); // nesne.metot is not a constructor
}

Günümüzde kurucu fonksiyon yerine genellikle class sözdizimi kullanılır. Sınıflar ve kalıtım için JavaScript class rehberine ve prototip kalıtımı yazısına bakın.

Karşılaştırma tablosu

Karşılaştırma tablosu: Tanımdan önce çağrılabilir mi, Kendi this bağlaması, arguments nesnesi, new ile kurucu
ÖzellikDeclarationExpressionArrow function
Tanımdan önce çağrılabilir miEvet (hoisting)HayırHayır
Kendi this bağlamasıVar, çağrıya göreVar, çağrıya göreYok, dış kapsamdan alır
arguments nesnesiVarVarYok
new ile kurucuOlurOlurOlmaz (TypeError)
prototype özelliğiVarVarYok
call/apply/bind ile thisDeğişirDeğişirDeğişmez
Ad zorunlu muEvetHayırAd yok, değişkenden alınır
Generator olabilir miEvet (function*)Evet (function*)Hayır

Hangisini ne zaman kullanmalısınız?

  • Dosyanın ana yardımcı fonksiyonları için function declaration okunaklıdır ve dosyanın herhangi bir yerinden çağrılabilir.
  • Kısa callback'ler (map, filter, then) için arrow function hem kısadır hem de dıştaki this değerini korur. Ayrıntılar callback fonksiyonlar yazısında.
  • Nesne ve sınıf metotları için kısa metot yazımını (metot() {}) kullanın; arrow function'ı metot olarak yazmayın.
  • Kendini çağıran ama bir değişkene atanacak fonksiyonlarda adlı fonksiyon ifadesi işinizi görür.
  • Kurucu gerekiyorsa arrow function kullanamazsınız; class ya da normal fonksiyon gerekir.

Sık yapılan hatalar

  • Fonksiyon ifadesini tanımlandığı satırdan önce çağırmak: var ile TypeError, let/const ile ReferenceError alırsınız.
  • Arrow function'dan nesne döndürürken parantezi unutmak: Sonuç undefined olur.
  • Nesne metodunu arrow function ile yazıp this ile nesneye ulaşmayı beklemek.
  • Arrow function içinde arguments kullanıp kendi argümanlarını aldığını sanmak.
  • Arrow function'ı new ile çağırmak ya da bind() ile this değerini değiştirmeye çalışmak.

Sık Sorulan Sorular

Arrow function normal fonksiyondan daha mı hızlı?

Kaynak belgelerde arrow function'ın normal fonksiyondan hızlı olduğuna dair bir bilgi yoktur. Seçimi performansa göre değil, davranışa göre yapın: this, arguments ve kurucu ihtiyacı hangisini gerektiriyorsa onu kullanın.

Fonksiyon bildirimi ile fonksiyon ifadesi arasındaki temel fark nedir?

MDN'ye göre en temel fark fonksiyon adıdır: İfadede ad isteğe bağlıdır ve yalnızca gövde içinde görünür. Pratikteki en önemli sonuç ise hoisting'dir; bildirim tanımdan önce çağrılabilir, ifade çağrılamaz.

Arrow function içinde this neden undefined ya da beklenmedik bir değer?

Çünkü arrow function kendi this değerini üretmez; tanımlandığı yerdeki this değerini görür. Dosyanın en üstünde tanımlanan bir arrow function, en üst düzeydeki this değerini kullanır; bu değer ortama göre değişir. Ayrıntılı tablo this rehberinde.

Arrow function generator olabilir mi?

Hayır. Arrow function gövdesinde yield kullanılamaz, bu yüzden generator olarak yazılamaz. Generator için function* kullanın; konu generator ve iterator yazısında ele alınıyor.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: JavaScript öğrenme rehberi · this anahtar kelimesi · Closure nedir · Default ve rest parametreler · Hoisting ve TDZ · JavaScript nedir