Menu
Playground'da Dene

JavaScript Higher-Order Functions: map, filter, reduce

JavaScript'te higher-order function mantığı: fonksiyonu argüman olarak geçirmek, fonksiyondan fonksiyon döndürmek ve her gün kullanacağın map, filter, reduce kalıpları.

Fonksiyonlar Birer Değerdir

JavaScript'te fonksiyonlar, sayı veya string gibi tıpkı diğer değerler gibidir. Bir değişkene atayabilir, bir dizinin içine koyabilir, başka bir fonksiyona argüman olarak geçirebilir ya da bir fonksiyondan geri döndürebilirsin. Yalnızca bu özellik bile bambaşka bir programlama tarzının kapısını aralıyor:

index.js
Output
Click Run to see the output here.

Fonksiyonları birer değer gibi düşünmeye alıştığınızda, higher order function kavramı bir anda gizemini kaybediyor. Higher order function dediğimiz şey aslında çok basit: ya argüman olarak bir fonksiyon alan, ya geriye bir fonksiyon döndüren, ya da ikisini birden yapan fonksiyonlara verilen isim. Tanımı bu kadar.

Fonksiyonu Argüman Olarak Geçirmek

En sık karşılaştığımız kullanım şekli bu: bir callback alıp onu sizin yerinize çalıştıran fonksiyonlar. Aslında farkında olmadan bunları zaten kullanıyorsunuz.

index.js
Output
Click Run to see the output here.

forEach bir higher-order fonksiyondur — senin fonksiyonunu alır ve her eleman için bir kez çağırır. setTimeout da higher-order bir fonksiyondur — fonksiyonunu alır, belirli bir gecikmeden sonra çalıştırır. Sen sadece ne yapılacağına odaklanırsın; ne zaman ve kaç kez çalışacağını onlar halleder.

Kendi higher-order fonksiyonunu yazmak da aynı mantıkla çalışır. Aşağıda, verilen callback'i yalnızca koşul true olduğunda çalıştıran küçük bir örnek var:

index.js
Output
Click Run to see the output here.

action, içinde bir fonksiyon tutan sıradan bir parametre. action() diye çağırdığında, dışarıdan ne geçildiyse o çalışır.

Pratikte Kullanacağın Üçlü: map, filter, reduce

Dizilerin (array) üzerinde, yazacağın for döngülerinin büyük kısmının yerini alan higher-order metotlar var. Bu üçünü kavradığında, günlük kodun önemli bir bölümü hem kısalır hem de çok daha okunaklı olur.

map — her elemanı dönüştür

index.js
Output
Click Run to see the output here.

map, her eleman için fonksiyonunuzu bir kez çağırır ve dönen değerleri yeni bir dizide toplar. Uzunluk aynı kalır, içerik dönüşür. Orijinal dizi değişmez.

filter — şartı sağlayanları alın

index.js
Output
Click Run to see the output here.

filter, callback fonksiyonundan true dönen elemanları tutar, gerisini atar. Geriye yeni bir dizi döner; eleman sayısı azalmış olabilir.

reduce — diziyi tek bir değere indirgemek

index.js
Output
Click Run to see the output here.

reduce ise bu işin çok amaçlı aracı. Callback fonksiyonu, o ana kadar birikmiş değeri (accumulator) ve sıradaki elemanı alır; sen ne return edersen bir sonraki adımın accumulator'ı o olur. İkinci argüman (burada 0) başlangıç değeridir.

Bunları zincirleyebilirsin de. İşte bu yaklaşımın asıl kazandırdığı nokta burada ortaya çıkıyor:

index.js
Output
Click Run to see the output here.

Yukarıdan aşağıya tek nefeste okunuyor: önce ödenmiş siparişleri al, fiyatı çek, topla. Ne döngü var, ne sayaç, ne de "acaba bir eksik mi saydım" derdi.

Fonksiyondan Fonksiyon Döndürmek

Higher-order konusunun diğer yarısı. Yani başka bir fonksiyon üretip geri döndüren fonksiyonlar:

index.js
Output
Click Run to see the output here.

multiplyBy(2) bir kez çalışır ve sana yepyeni bir fonksiyon döndürür. O yeni fonksiyon hâlâ factor değerini hatırlar — işte buna closure deniyor ve ona ayrı bir bölümde değineceğiz. Şimdilik aklında kalması gereken şey şu: multiplyBy'ı farklı argümanlarla çağırarak aynı kalıptan türetilmiş, farklı işlere özelleşmiş fonksiyonlar elde edebilirsin.

Bu desen gerçekten her yerde karşına çıkıyor:

index.js
Output
Click Run to see the output here.

Tek bir tanım, iki kullanılabilir fonksiyon. warn ve info'yu elle yazıp senkron tutmaya çalışmaktan çok daha temiz.

İsimli fonksiyonlar mı, inline callback mi?

Callback'i ya inline bir arrow function olarak geçebilirsin ya da fonksiyonu adıyla verebilirsin. İkisi de çalışır — hangisi daha okunaklı duruyorsa onu seç:

index.js
Output
Click Run to see the output here.

isEven (parantezsiz) yazdığında, fonksiyonun kendisini devretmiş olursun. () eklersen fonksiyon anında çalışır ve dönen değeri geçirmiş olursun — yeni başlayanların sıkça düştüğü bir tuzak:

nums.filter(isEven);     // doğru: fonksiyonu geçirir
nums.filter(isEven());   // yanlış: isEven'i argümansız çağırır, sonucu geçirir

Callback fonksiyon birkaç satırı geçmeye başladıysa dışarı çıkarıp ona bir isim verin. Etraftaki kod da genelde bundan kazançlı çıkar.

Uçtan Uca Bir Örnek

Higher-order function'lar asıl gücünü küçük parçaları bir araya getirdiğinizde gösteriyor. Diyelim ki elinizde bir ürün listesi var ve stokta olan uygun fiyatlı ürünlerin adlarını büyük harfle almak istiyorsunuz:

index.js
Output
Click Run to see the output here.

Her yardımcı fonksiyonun tek bir işi var. Her dizi metodu tek bir dönüşüm yapıyor. Pipeline, nasıl döngü kurulacağını değil, ne istediğinizi anlatan bir spesifikasyon gibi okunuyor.

Ne Zaman Kullanmamalı?

Higher-order metotlar harika — ama her durumda döngülerin yerine geçmezler:

  • Ortada bir yerde durmanız gerekiyorsa, forEach içinden çıkmaya çalışmak yerine for ya da for...of ile break kullanmak çok daha temiz olur.
  • Callback içinde async bir iş yapıyorsanız, map ve forEach bunu await etmez. Bu durumda ya for...of ile await kullanın ya da map ile Promise.all'a başvurun.
  • Callback paylaşılan state'i değiştiriyorsa (mutasyon), zaten bu stilin güçlü yanlarından uzaklaşıyorsunuz demektir. Ya normal bir döngüye dönün ya da yeni değer döndürecek şekilde refactor edin.

Yerinde kullanıldığında map, filter ve reduce günlük kodunuzdaki döngü tekrarının büyük kısmını ortadan kaldırır. Ama her yere sıkıştırmaya çalışırsanız okunabilirliği baltalamaya başlarlar. Niyeti en net ortaya koyan aracı seçin.

Sırada: Nesneler

Üzerine inşa edilecek tek değerli şey fonksiyonlar değil. İlgili veri ve davranışları bir arada tutmak için JavaScript'in baş aktörü nesnelerdir — ve az önce filter'layıp map'lediğiniz dizilerin içi büyük ihtimalle zaten bunlarla doluydu. Bir sonraki sayfanın konusu da bu.

Sıkça Sorulan Sorular

JavaScript'te higher-order function ne demek?

Higher-order function, şu iki işten en az birini yapan fonksiyondur: ya başka bir fonksiyonu argüman olarak alır, ya da sonuç olarak bir fonksiyon döndürür. Array.prototype.map, setTimeout ve addEventListener bunun tipik örnekleri — hepsi bir callback alıyor ve onu senin yerine çağırıyor.

map, filter ve reduce arasındaki fark nedir?

map dizideki her elemanı dönüştürür ve aynı uzunlukta yeni bir dizi döndürür. filter ise callback'in truthy döndürdüğü elemanları tutar, sonuç genelde daha kısa bir dizi olur. reduce diziyi tek bir değere indirger; elemanları teker teker birleştirerek sonuca ulaşır. Üçü de callback alan higher-order function'lardır.

Neden bir fonksiyondan başka bir fonksiyon döndürelim?

Aynı mantığı tekrar yazmadan, küçük ve ayarlanabilir yardımcı fonksiyonlar üretmek için. Örneğin multiplyBy(n) içinde n ile çarpan yeni bir fonksiyon döner; böylece multiplyBy(2) ve multiplyBy(10) tek bir tanımdan iki özel fonksiyon verir. Bu kalıp closure'a dayanır ve event handler'larda, middleware'lerde ve utility kütüphanelerinde bolca karşına çıkar.

Coddy ile kodlamayı öğren

BAŞLA