BildiÄiniz gibi JavaScriptâte fonksiyonlar deÄerdir.
Her deÄerin bir tipi olduÄuna göre fonksiyonun tipi nedir?
JavaScriptâte fonksiyon bir objedir.
Daha iyi görselleyebilmek adına fonksiyonlara âaksiyon objeleriâ denebilir. Sadece çaÄırarak deÄil, obje olarak da davranabilirsiniz: özellik ekleme çıkarma, referans paslama vs.
ânameâ özelliÄi
Fonksiyon objelerinin kullanıÅlı özellikleri bulunmaktadır.
ÃrneÄin, fonksiyonun ismi ânameâ özelliÄi ile alınabilir.
function selamVer() {
alert("Selam");
}
alert(selamVer.name); // selamVer
ânameâ özelliÄi atama o kadar akıllıdır ki, fonksiyon tanımlama ifadelerindeki ismi bile doÄru alır.
let selamVer = function() {
alert("Selam");
}
alert(selamVer.name); // selamVer
Hatta atama varsayılan deÄer ile yapıldıÄında bile çalıÅır:
function f(selamVer = function() {}) {
alert(selamVer.name); // selamVer (çalıÅtı!)
}
f();
Tanımda bu özelliÄe âbaÄlamsal isimâ denir. EÄer fonksiyonda böyle bir Åey yoksa, tanımlama bunu içerikten alır.
Nesne metodlarının da isimleri vardır:
let kullanici = {
selamVer() {
// ...
},
yolcuEt: function() {
// ...
}
}
alert(kullanici.selamVer.name); // selamVer
alert(kullanici.yolcuEt.name); // yolcuEt
Burada bir sihir yoktur. İsmin çıkarılamadıÄı birçok durum meydana gelebilir.
Böyle durumlarda aÅaÄıdaki gibi boÅ dönerler:
// Dizinin içerisinde fonksiyon yaratılması
let arr = [function() {}];
alert( arr[0].name ); // <boÅ>
// motorun doÄru ismi bulmasına imkan yok bundan dolayı boÅ dönüyor.
Pratikte çoÄu fonksiyonun ismi bulunmaktadır.
âlengthâ özelliÄi
âlengthâ adında ayrı bir özellik daha bulunmaktadır. Bu özellik fonksiyon parametrelerinin sayısını döndürür:
function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}
alert(f1.length); // 1
alert(f2.length); // 2
alert(many.length); // 2
GördüÄünüz gibi geriye kalan parametresi ... sayılmamaktadır.
length özelliÄi bazen diÄer fonksiyonların üzerinde çalıÅan fonksiyonlara bir iç bakıŠoluÅturur.
Mesela, aÅaÄıdaki kodda sorfonksiyonu soru parametresi alır ve belirli olmayan sayıda isleyici fonksiyonunu çaÄırır.
Kullanıcı cevap verdiÄinde isleyici(handler) çaÄırılır. İki türlü iÅleyici gönderilebilir:
- Argümansız fonksiyon, sadece pozitif cevaplarda çaÄırılır.
- Argümanlı fonksiyonlar, cevap alınan her durumda çaÄırılır.
Mantık Åu Åekildedir; cevap pozitif olduÄunda argüman almayan isleyici calisir, fakat evrensel isleyiciye de izin verir.
isleyiciâlerin doÄru çalıÅması için, length özelliÄinden faydalanılabilir.
function sor(soru, ...isleyiciler) {
let dogruMu = confirm(soru);
for(let isleyici of isleyiciler) {
if (isleyici.length == 0) {
if (dogruMu) isleyici();
} else {
isleyici(dogruMu);
}
}
}
// Olumlu cevap için, her ikisi çalıÅırken
// Olumsuz cevap için sadece ikincisi çalıÅmaktadır.
sor("Soru?", () => alert('Evet dedin'), sonuc => alert(sonuc));
Bu duruma polymorphism denilmektedir. Bu argümanlara tiplerine göre farklı davranma olayıdır. Bu bizim durumumuzda lengthâe baÄlıdır. Bu fikir JavaScript kütüphanelerinde de kullanılmaktadır.
ÃzelleÅtirilmiŠözellikler
Kendi özelliÄinizi eklemek de mümkündür.
ÃrneÄin aÅaÄıda counter özelliÄi ile toplan çaÄrı sayısı tutulmaktadır:
function selamVer() {
alert("Selam");
// Kaç defa çaÄırıldıÄını tutar.
selamVer.counter++;
}
selamVer.counter = 0; // ilk deÄer
selamVer(); // Selam
selamVer(); // Selam
alert( `selamVer ${selamVer.counter} defa çaÄırılmıÅtır` ); // selamVer 2 defa çaÄırılmıÅtır.
Fonksiyona atanan selamVer.counter = 0 selamVer fonksiyonunun içerisinde counter deÄiÅkenini tanımlamaz. DiÄer bir deyiÅle counter özelliÄi ile let counter birbirinden tamamen farklı Åeylerdir.
Fonksiyona obje gibi davranıp özellik eklenebilir. Bu çalıÅmasında bir etki yaratmaz. DeÄiÅkenler fonksiyon özelliklerini kullanmaz, keza fonksiyon özellikleri de deÄiÅkenleri kullanmaz.
Fonksiyon özellikleri closure kullanılarak tekrardan yazılabilir. ÃrneÄin yukarıdaki sayaç örneÄini Closure kullanarak tekrardan yazacak olursak:
function makeCounter() {
// let count = 0 yazmak yerine
function counter() {
return counter.count++;
};
counter.count = 0;
return counter;
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
count artık fonksiyonun içerisinde bulunur, dıŠortamda deÄil.
Closure kullanmak iyi mi kötü mü?
EÄer sayacâın deÄeri dıÅarıdaki deÄiÅkende bulunuyorsa, dıÅta bulunan kod buna eriÅemez. Sadece içteki fonksiyon bunu modifiye edebilir. Bu da anca fonksiyona baÄlıysa gerçekleÅebilir:
function makeCounter() {
function counter() {
return counter.count++;
};
counter.count = 0;
return counter;
}
let counter = makeCounter();
counter.count = 10;
alert( counter() ); // 10
Bundan dolayı asıl önemli olan sizin hangi Åekilde kullanmak istediÄiniz.
İsimlendirilmiÅ Fonksiyon İfadeleri ( Named Function Expression â NFE )
İsimlendirilmiÅ fonksiyon ifadeleri , NFE, daha önce kullandıÄımız Fonksiyon İfadelerinin isimlendirilmiÅ halidir.
ÃrneÄin, sıradan bir Fonksiyon İfadesi incelenecek olursa:
let selamVer = function(kim) {
alert(`Selam, ${kim}`);
};
⦠isimlendirilirse:
let selamVer = function func(kim) {
alert(`Merhaba, ${kim}`);
};
Peki buradaki mantık ne? Ayrıca bir "func" eklemenin ne anlamı var?
Tekrar etmek gerekirse, hala bir Fonksiyon İfadeniz var. Fonksiyon ifadesine "function" 'dan sonra "func" eklemek bu fonksiyonu Fonksiyon Tanımı haline getirmez çünkü hala bir atama operasyonu ile tanımlanmıÅtır.
Böyle bir isim eklemek hiçbir soruna neden olmaz.
Fonksiyon hala selamVer() Åeklinde kullanılabilir:
let selamVer = function func(kim) {
alert(`Selam, ${kim}`);
};
selamVer("Ahmet"); // Selam, Ahmet
func ismine ait iki tane özellik vardır:
- Bu Åekilde fonksiyonun kendisine içerisinden referans vermek mümkündür.
- Fonksiyonun dıÅından eriÅilemez.
ÃrneÄin, selamVer fonksiyonu eÄer bir parametre olmadan çaÄırılırsa kendisini "Misafir" ile tekrardan çaÄırabilir.
let selamVer = function func(kim) {
if (kim) {
alert(`Selam, ${kim}`);
} else {
func("Misafir"); // kendisni yeniden çaÄırabilir.
}
};
selamVer(); // Selam, Misafir
// Fakat aÅaÄıdaki çalıÅmayacaktır:
func(); // func tanımlı deÄildir. ( Fonksiyonun dıÅından eriÅilemez.)
Peki neden func kullanıyoruz? Sadece selamVer kullansak olmaz mı?
Aslında çoÄu durumda olur:
let selamVer = function(kim) {
if (kim) {
alert(`Selam, ${kim}`);
} else {
selamVer("Misafir");
}
};
Buradaki problem selamVerâin deÄeri deÄiÅebilir. Fonksiyon diÄer bir deÄiÅkene gidebilir ardından hatalar vermeye baÅlar.
let selamVer = function(kim) {
if (kim) {
alert(`Selam, ${kim}`);
} else {
selamVer("Misafir");
}
};
let hosGeldin = selamVer;
selamVer = null;
hosGeldin(); //Artık selamVer çaÄırılamaz.
Bunun olmasının nedeni fonksiyonun selamVerâi dıŠortamdan alıyor olmasıdır. Yerel bir selamVer bulunmadıÄından dıÅtaki deÄiÅken kullanılmaktadır. O anda da dıÅta bulunan selamVer nullâdur.
Opsiyonel olarak konulan isim tam olarak Fonksiyon İfadesinin bu problemini çözer.
Bunu kullanarak kod Åu Åekilde düzeltilebilir:
let selamVer = function func(kim) {
if (kim) {
alert(`Selam, ${kim}`);
} else {
func("Misafir"); // Åimdi hepsi doÄru Åekilde çalıÅır.
}
};
let hosGeldin = selamVer;
selamVer = null;
hosGeldin(); // Selam, Misafir (iç çaÄrı çalıÅır)
Åimdi çalıÅır, bunun nedeni "func"'in lokal fonksiyon olmasındandır. DıÅarıdan alınmaz ( dıÅarıdan görünmez de ). Bu Åekilde yazıldıÄında var olan fonksiyonu referans vereceÄi garantidir.
DıÅta bulunan kod hala selamVer veya hosGeldin deÄiÅkenlerine sahiptir. DıÅtaki deÄiÅkenlere bir Åey olsa bile func"iç fonksiyon ismi"'dir. Kendisini gizli biçimde çaÄırabilir.
âiçsel isimâ olarak tanımlanan özellik sadece Fonksiyon İfadeleri için geçerlidir. Fonksiyon Tanımlarında çalıÅmaz. Fonksiyon tanımları için âiçselâ bir isim ekleme yöntemi yoktur.
Bazen güvenli bir isme ihtiyaç duyulduÄunda Fonksiyon Tanımı tekrardan İsimlendirilmiÅ Fonksiyon İfadesi Åekline getirilir.
Ãzet
Fonksiyonlar objedir.
Ãzellikleri Åu Åekildedir:
nameâ Fonksiyon ismi. Sadece fonksiyon tanımlama da deÄil, atamalar ve obje özellikleri için.lengthâ Fonksiyon tanımındaki argüman sayısı, geriye kalan parametreleri ( ⦠) sayılmaz.
EÄer fonksiyon Fonksiyon Tanımı yöntemi ile ( ana akıÅta olmayan ) tanımlanmıÅsa ve isim taÅıyorsa buna İsimlendirilmiÅ Fonksiyon Tanımı denir. İsim içersiinde veya recursive çaÄrılarda kullanılabilir.
Fonksiyonlar ayrıca baÅka özelliklerde taÅıyabilirler. ÃoÄu bilinen JavaScript kütüphanesi bu özelliÄi ziyadesiyle kullanır.
Genelde bir âanaâ fonksiyon ve bu fonksiyona baÄlı birçok âyardımcıâ fonksiyon tanımlarlar. ÃrneÄin jquery $ adında bir fonksiyon oluÅturur. lodash fonksiyonu _ adında bir fonksiyon oluÅturu. Ardıncan _.clone, _.keyBy gibi özellikleri ekler. Dökümantasyonâu inceleyebilirsiniz. Aslında, global alanda baya temiz çalıÅırlar. Böylece bir kütüphane bir tane global deÄiÅken vermiÅ olur. Bu da isimlerin birbiriyle çakıÅmasını engeller.
Bundan dolayı bir fonksiyon kendince bir iÅ yapabilir ve özellikleri vasıtasıyla baÅka fonksiyonalitelere de sahip olabilir.
Yorumlar
<code>kullanınız, birkaç satır eklemek için ise<pre>kullanın. EÄer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)