JavaScript fonksiyon yönelimli bir dildir. Ãok baÄımsızlık verir. Fonksiyon bir yerde yaratılıp sonra baÅka bir deÄiÅkene atanarak diÄer bir fonksiyona argüman olarak gönderilebilir ve sonra tamamen farklı bir yerden çaÄrılabilir.
BildiÄiniz gibi fonksiyon kendi dıÅında olan deÄiÅkenlere ulaÅabilir ve bu özelliklik oldukça fazla kullanılır.
Peki ya dıÅarıdaki deÄiÅken deÄiÅirse? Fonksiyon en son deÄerini mi alacak yoksa yaratıldıÄında var olan deÄeri mi?
Ayrıca diyelim ki fonksiyon baÅka bir yere gönderildi ve oradan çaÄrıldıÄında ne olur, yeni yerinden dıÅarıda bulunan deÄiÅkenlere eriÅebilir mi?
Bu sorulara farklı diller farklı cevaplar vermektedir, bu bölümde JavaScriptin bu sorulara cevabını öÄreneceksiniz.
Birkaç soru
Ãrnek olması amacıyla iki soru formülize edilecek olursa, sonrasında içsel mekanizması parça parça incelenecektir, ileride daha karmaÅık sorulara cevap verebilirsiniz.
-
selamVerfonksiyonu dıÅarıda bulunanisimdeÄiÅkenini kullanmaktadır. Fonksiyon çalıÅtıÄında, hangiisimdeÄiÅkeni kullanılacaktır?let isim = "Ahmet"; function selamVer() { alert("Merhaba, " + isim); } isim = "Mehmet"; selamVer(); // "Ahmet" mi yoksa "Mehmet" mi gösterilecek?Böyle durumlara tarayıcı ve sunucu tabanlı geliÅtirmelerde oldukça sık karÅılaÅılır. Bir fonksiyon yaratıldıÄı anda deÄil de daha sonra çalıÅmak üzere programlanabilir. ÃrneÄin bir kullanıcı aksiyonu veya aÄ Ã¼zerinden istekler bu gruba girer.
Ãyleyse soru: son deÄiÅiklikleri alır mı?
-
calisanYaratdiÄer bir fonksiyon yaratır ve bunu döner. Bu yeni fonksiyon herhangi bir yerden çaÄrılabilir. Peki yaratıldıÄı yerin dıÅındaki deÄiÅkenlere veya çaÄrılan yerin dıÅındaki deÄiÅkenlere veya ikisine birden eriÅebilecek mi?function calisanYarat() { let isim = "Mehmet"; return function() { alert(isim); }; } let isim = "Zafer"; // fonksiyon yarat let is = calisanYarat(); // çaÄır is(); // burada "Mehmet" mi yoksa "Zafer" mi gösterilecek ?
Sözcüksel ortam ( Lexical Environment )
Ne olduÄunu anlamak için önce âdeÄiÅkenâ'in tekniksel anlamı üzerinde tartıÅmak lazım
JavaScriptâte çalıÅan her fonksiyon, kod bloÄu bir bütün olarak âSözcüksel Ortamâ adında bir objeye sahiptir.
Bu âSözcüksel Ortamâ iki bölümden oluÅur:
- Ortam Kaydı â tüm yerel deÄiÅkenleri ve özelliklerini ( ve ek özellikleri
thisgibi ) tutan objedir. - DıŠSözcüksel Ortamâa referans genelde süslü parantezin dıÅındaki kod ile ilintilidir.
Ãyleyse âdeÄiÅkenâ içsel objedeki bir özelliktir, çevresel kayıtlar. âdeÄiÅkeni almak veya deÄiÅtirmekâ demek âo objenin özelliÄini almak veya deÄiÅtirmekâ demektir.
ÃrneÄin, aÅaÄıdaki kodda sadece bir tane Sözcüksel Ortam bulunmaktadır:
Buna evrensel sözcük ortamı denilmektedir, kodun tamamıyla alakalıdır. Tüm tarayıcılarda <script> etiketleri aynı evrensel ortamı paylaÅır.
Yukarıdaki görselde, dikdörtgen ile gösterilen Ãevresel Kayıt ( deÄiÅken kaynaÄı ) anlamına gelir ve ok iÅareti dıÅsal referanstır. Evrensel Sözcük Ortamından daha dıŠortam bulunmamaktadır. Yani null dur.
AÅaÄıda let deÄiÅkenlerinin nasıl çalıÅtıÄı görsel ile açıklanmıÅtır:
SaÄ tarafta bulunan dikdörtgenler evrensel Sözcük Ortamının çalıÅırkenki deÄiÅikliklerini gösterir.
- Kod çalıÅmaya baÅladıÄında, Sözcüksel Ortam boÅtur.
let ifadetanımlaması görünür. İlk baÅta bir deÄeri bulunmamaktadır bundanundefinedolarak saklanır.ifadeâye deÄer atanır.ifadeyeni bir deÄere referans olur.
Her Åey çok basit görünüyor deÄil mi?
Ãzetlemek gerekirse:
- DeÄiÅken özel bir iç objenin özelliÄidir. Bu obje o anda çalıÅan kod, fonksiyon ile baÄlantılıdır.
- DeÄiÅkenlerle çalıÅmak aslında o objenin özellikleri ile çalıÅmak demektir.
Fonksiyon tanımı
Fonksiyon tanımları özeldir. let deÄiÅkenlerine nazaran çalıÅtırıldıklarında deÄil de Sözcüksel Ortam yaratıldıÄında iÅlenirler, bu da kodun baÅladıÄı zamandır.
⦠Ve bundan dolayı bir fonksiyon tanımından önce çaÄırılabilir.
AÅaÄıdaki kodda Sözcüksel Ortam baÅlangıçta boÅ deÄildir. sayâe sahiptir çünkü bu bir fonksiyon tanımıdır. Sonrasında ifade alır ve bunu let ile tanımlar:
İç ve dıŠSözcüksel Ortamlar
say() fonksiyonu çaÄrısı sırasında dıŠdeÄiÅkenler çaÄrılır, bu olaya daha detaylı bakacak olursak.
Fonksiyon ilk çalıÅtıÄında yeni bir Sözcüksel Ortam otomatik olarak yaratılır. Bu tüm fonksiyonlar için genel bir kuraldır. Bu Sözcüksel Ortam yerel deÄiÅkenlerin tutulması ve çaÄrının tüm parametrelerini tutar.
say("Ahmet") fonksiyonu çalıÅtıÄı sırada Sözcüksel Ortam aÅaÄıdaki gibi olur:
Fonksiyon çaÄrıldıÄında ise iki tane sözcüksel ortam bulunmaktadır: içte olan(fonksiyon çaÄrısı için) ve dıÅta olan(evrensel):
- İçte olan sözcüksel ortam
sayfonksiyonunun o anki durumuna bakar, o anda tekadidegiskeni bulunmaktadır.say("Ahmet")çaÄrıldıÄından dolayıidideÄiÅkeninin deÄeri"Ahmet"olur. - DıŠSözcük Ortamı ise bu durumda Evrensel Sözcük Ortamıdır.
İç Sözcük ortamı outer ile DıŠSözcük Ortamına referans olur.
Kod deÄiÅkene ulaÅmak istediÄinde â önce İç Sözcük ortamında arar, daha sonra dıŠsözcüm ortamına bakar ve daha sonra daha dıÅtakine bakar bu Åekilde zincirin en sonuna kadar devam eder
EÄer deÄiÅken hiçbir yerde bulunamazsa, sıkı modda hata verir. use strict kullanılmazsa tanımsız deÄiÅken yeni bir global deÄiÅken yaratır.
Arama olayı bizim yazdıÄımız kodlarda nasıl iÅliyor buna bakalım:
sayiçindekialertadideÄiÅkenine eriÅmek istediÄinde, anında Sözcük Ortamında bulabilir.ifadeâye eriÅmek istediÄinde önce fonksiyonun içine bakar fakat orada da bulamayacaÄındanouterreferansı takip ederek evrensel sözcük ortamından bu deÄiÅkene eriÅebilir.
Åimdi bölümün ilk baÅında sorulan sorulara cevap bulunabilir.
Bir fonksiyon dıÅta bulunan deÄiÅkenin en son deÄerini alır.
Bunun nedeni tanımlanan mekanizmadan dolayıdır. Eski deÄiÅkenler bir yere kaydedilmezler. Fonksiyon bunları istediÄinde iç sözcük ortamından veya dıŠsözcük ortamından o anki deÄeri alır.
Bundan dolayı ilk sorunun cevabı Mehmet olacaktır:
let adi = "Ahmet";
function selamVer() {
alert("Merhaba, " + adi);
}
adi = "Mehmet"; // (*)
selamVer(); // Mehmet
ÃalıÅma akıÅı Åu Åekildedir:
- Evrensel Sözcük ortamında
adi:"Ahmet"bulunmaktadır. (*)satırında evrensel deÄiÅken deÄiÅir, Åimdiadi:"Mehmet"bulunmaktadır.selamVer()fonksiyonu çalıÅtıÄındaadideÄiÅkenini dıÅarıdan alır. BudıÅsözcüksel ortamda deÄiÅkenin deÄeri"Mehmet"tir.
Fonksiyon Sözcük Ortamı her fonksiyon çaÄrıldıÄında yeniden yaratılır.
EÄer fonksiyon bir kaç defa çaÄırılırsa her çaÄrıldıÄında kendine ait ayrı bir Sözcüksel Ortamı olur, tabi bu ortam o anki çaÄırılmaya ait yerel deÄiÅkenleri ve parametreleri tutar.
âSözcüksel Ortamâ bir Åartname objesidir. Bu objeyi alıp düzenleyemezsiniz veya doÄrudan kullanamazsınız. JavaScript motoru yapabildiÄince bu deÄiÅkenleri optimize etmeye çalıÅır, kullanılmayan deÄiÅkenleri saf dıÅı bırakabilir fakat görülen davranıÅları yukarıda anlatıldıÄı gibi olmalıdır.
İç içe fonksiyonlar
Bir fonksiyon diÄer bir fonksiyon içerisinde yaratılırsa buna iç içe fonksiyon denir.
Teknik olarak bu mümkündür.
Kodu organize etmek için Åu Åekilde kullanabilirsiniz:
function selamYolcu(adi, soyadi) {
// yardımcı iç içe fonksiyon.
function tamIsim() {
return adi + " " + soyadi;
}
alert( "Merhaba, " + tamIsim() );
alert( "Güle Güle, " + tamIsim() );
}
iç içe fonksiyon tamIsim() kullanım kolaylıÄı saÄlaması amacıyla yapılmıÅtır. DıÅta bulunan deÄiÅkenlere eriÅebilir ve tam ismi döndürebilir.
Daha ilginci, iç içe bir fonksiyon geri döndürülebilir: Bu yeni objenin bir özelliÄi olarak veya sonucun kendisi dönebilir. Sonra baÅka yerde kullanılabilir. Nerede olduÄu önemli olmaksızın, hala aynı dıŠdeÄiÅkene eriÅebilir.
Bunun örneÄi yapıcı ( constructor ) fonksiyondur ( Yapıcı, "new" operatörü bölümünden inceleyebilirsiniz. )
// yapıcı fonksiyon yeni bir obje dönderir.
function Kullanici(isim) {
// obje metodu iç içe fonksiyon olarak yaratıldı.
this.Kullanici = function() {
alert(isim);
};
}
let kullanici = new Kullanici("Ahmet");
kullanici.selamYolcu(); // metod dıÅarıda bulunan "isim" deÄiÅkenine eriÅebilir.
Fonksiyonun döndürülmesi örneÄi:
function sayacUret() {
let sayac = 0;
return function() {
return sayac++; // dıÅarıda bulunan sayac deÄiÅkenine eriÅimi bulunmaktadır.
};
}
let sayac = sayacUret();
alert( sayac() ); // 0
alert( sayac() ); // 1
alert( sayac() ); // 2
sayacUret örneÄine bakılacak olursa. âsayacâ fonksiyonunu bir sonraki sayı ile döndürür. Basit olmasının yanında biraz modifiye edilmiÅ hali pratikte kullanılmaktadır pseudorandom number generator. Yani çok suni bir örnek deÄildir.
Peki sayaç içeride nasıl çalıÅmakta?
İçteki fonksiyon çalıÅtıÄında sayac++ içeriden dıÅarıya kadar sayac deÄiÅkenini arar. Yukarıdaki örneÄe bakılacak olursa, sıralama Åu Åekilde olacaktır:
- İçte bulunan fonksiyonun yerel deÄiÅkenleri.
- DıÅta bulunan fonksiyonların deÄiÅkenleri.
- â¦Bu evrensel deÄiÅkenlere kadar gider.
sayac örneÄinde 2. adımda bulundu. DıÅtaki deÄiÅken deÄiÅtirildiÄinde, bulunduÄu yerde deÄiÅiklik olur. Bundan dolayı sayac++ dıÅtaki deÄÅikeni bulur ve dıÅtaki deÄiÅkenin Sözcüksel Ortamında bu deÄiÅkenin deÄerini deÄiÅtirir. Sanki let sayac = 1 yapıyormuÅ gibi.
Size iki tane sorum var:
sayacUretâe ait olmayan bir koddansayacdeÄiÅkeni sıfırlanabilir mi? Mesela yukarıdaki örnektealertsonrasında.- EÄer
sayacUret()'i bir kaç defa çaÄırırsanız â birçoksayacfonksiyonu döndürür. Bunlar birbirinden baÄımsız mıdır yoksa aynısayacâı mı kullanılar?
Okumaya devam etmeden yukarıdaki sorulara cevap vermeye çalıÅın.
â¦Bitti mi?
Peki o zaman, Åimdi cevaplar.
- Hayır sıfırlayamaz.
sayacyerel bir deÄiÅkendir ve dıÅarıdan eriÅilemez. - Her
sayacUretçaÄrısı o fonksiyona ait Sözcüksel Ortam üretir, bunun da kendine aitsayacdeÄiÅkeni bulunmaktadır. ÃyleysesayacdeÄiÅkenleri her fonksiyon için baÄımsızdır denebilir.
ÃrneÄin:
function sayacUret() {
let sayac = 0;
return function() {
return sayac++; // dıÅarıda bulunan sayac deÄiÅkenine eriÅimi bulunmaktadır.
};
}
let sayac1 = sayacUret();
let sayac2 = sayacUret();
alert( sayac1() ); // 0
alert( sayac1() ); // 1
alert( sayac2() ); // 0 (independant)
Muhetemelen, aklınızda dıŠdeÄiÅkenlerin nasıl çalıÅtıÄı açıklıÄa kavuÅtu. Fakat daha karmaÅık olaylar için daha derine inmeye gerek var.
Detaylı Åekilde ortamların incelenmesi.
Åu anda clouseâların genel olarak nasıl çalıÅtıÄını biliyorsunuz, artık daha derinine inme vakti geldi.
AÅaÄıda sayacUret fonksiyonunun adımları gösterilmektedir, her Åeyi anladıÄınıza emin olun. Basamaklarda göreceÄiniz [[Environment]] henüz iÅlenmedi.
-
Kod çalıÅmaya baÅkadıÄında sadece bir tane Sözcüksel Ortam bulunmaktadır:
BaÅlangıçta sadece
sayacUretfonksiyonu bulunmaktadır, çünkü bu fonksiyon tanımıdır. Henüz çalıÅmadı.Tüm fonksiyonlar baÅlangıçta gizli bir
[[Environment]]deÄiÅkeni alırlar, bu yaratılmaya dair üretilecek Sözcüksel Ortama referans olur. Bunun hakkında henüz bilgi verilmedi, fakat teknik olarak bunu fonksiyonun nerede yaratıldıÄını bilmesi olarak anlayabilirsiniz.Burada
sayacUretEvrensel Sözcüksel Ortamda yaratıldı. Bundan dolayı[[Environment]]bu ortamın referansıdır.DiÄer bir deÄiÅle fonksiyon üretildiÄinde Sözcüksel Ortama ait bir âbaskıâ ile üretilir. Bu
[[Environment]]gizli bir özellik olarak burayı referans verir. -
Sonrasında kod
sayacUret()çaÄrısını yapıyor. AÅaÄıdasayacUret()'in ilk satırı çalıÅtıÄındaki durumu gösterilmektedir.sayacUret()fonksiyonu çaÄrıldıÄında, bu fonksiyonun deÄiÅkenlerini ve argümanlarını tutmak için Sözcüksel Ortam yaratılır.Her Sözcüksel Ortam iki Åeyi tutar:
-
Yerel deÄiÅkenlere ait Ortamsal Kayıtlar. Bu durumda
let sayacçalıÅtırıldıÄında yerel deÄiÅken olaraksayactutulmaktadır. -
DıŠsözcüksel referans, bu fonksiyonun
[[Environment]]'i dir. BuradasayacUretfonksiyonunun[[Environment]]'i evrensel sözcüksel ortama referans verir.
Ãyleyse Åimdi iki tane sözcüksel ortam bulunmaktadır: evrensel olan ve
sayacUretçaÄrısını yapan( dıŠreferans verir). -
-
sayacUret()fonksiyonu çalıÅtıÄında küçük bir iç fonksiyon yaratılır.Fonksiyonun nasıl yaratıldıÄı yani Fonksiyon Tanımıyla mı yoksa Fonksiyon İfadesiyle mi yaratıldıÄı önemli deÄildir. Tüm fonksiyonlar bulunduÄu sözcüksel ortama referans eden
[[Environment]]özelliÄi ile yaratılırlar. Bundan dolayı en küçük fonksiyon bile bu özelliÄi içerir.İçte olan yeni fonksiyon için
[[Environment]]deÄiÅkeni var olansayacUretâin Sözcüksel Ortamıdır.( DoÄduÄu yer )Dikkat ederseniz bu basamakta iç fonksiyon yaratıldı fakat çaÄırılmadı. İçindeki kod
function() { return sayac++; }çalıÅmadı, bu kod döndürülecek. -
ÃalıÅma devam ettiÄinde
sayacUret()biter, sonuc olarak ( küçük iç fonksiyon ) globalcounterdeÄiÅkenine atanıyor.Bu fonksiyonun sadece bir satır kodu var:
return sayac++, sadece bu çalıÅacaktır. -
sayac()çaÄrıldıÄında, âboÅâ bir Sözcüksel Ortam yaratılır. hiçbir yerel deÄiÅkeni yoktur. Fakatsayacâın[[Environment]]'i dıŠreferans olarak kullanılır. Bundan dolayı, daha önceden yapılansayacUret()'in deÄiÅkenlerine eriÅebilir. OluÅturulduÄu yer:DeÄiÅkene eriÅmesi gerekirse önce kendi yerel sözcüksel ortamına(boÅ), sonra daha önce çaÄrılan
sayacUret()'in sözcüksel ortamına, en son evrensel ortama bakar.sayaciçin arama yaptıÄında, en yakınındasayacUretâin sözcüksel ortamı bulunmaktadır.Buradaki hafıza yönetimine dikkat ederseniz.
sayacUret()çaÄrısı bittikten bir süre sonra, Sözcüksel ortam hafızada tutulur, çünkü içte bulunan fonksiyonun[[Environment]]'isayacUretâe referans vermektedir.Genel olarak, sözcüksel ortam objesi fonksiyon kullanılabilir olduÄu sürece yaÅar. Fonksiyon kullanılmadıÄında silinir.
-
sayac()fonksiyonu sadecesayacdeÄiÅkenini döndürmekle kalmaz, artırırda. Dikkat ederseniz deÄiÅiklik sadece âolduÄu yerdeâ yapıldı. Var olansayacdeÄiÅkeni bulunduÄu ortamda deÄiÅtirildi.Ãyleyse bir önceki adıma tek deÄiÅiklikle geri dönülmektedir â
sayacâın yeni deÄeri. Devam eden çaÄrılar da aynı Åekilde çalıÅırlar. -
Sonraki
sayac()da aynısını yapar.
BaÅlangıçta sorulan ikinci sorunun cevabı Åimdi açıklık kazanmıŠolmalı.
AÅaÄıda isim özelliÄi calisanUret() fonksiyonu tarafından bulunduÄu ortamdan kullanılmıÅtır:
Sonuç görüÅdüÄü gibi "Pete" olacaktır.
â¦Fakat eÄer calisanUret() fonksiyonu içerisinde let name tanımlanmamıŠolsaydı, bu durumda deÄiÅkeni arayıŠevrensel deÄiÅkenler ile devam edecekti ve bu durumda sonuç "John" olacaktı.
Genel programlama tanımlarında âclosureâ adında bir tanım bulunmaktadır. Bunun ile closure dıÅtaki deÄiÅkenleri hatırlayabilen ve bunlara eriÅebilen fonksiyon anlaÅılmalıdır. Bazı dillerde, bu mümkün deÄildir veya fonksiyonun özel bir biçimde yazılması gerekmektedir. Fakat yukarıda görüldüÄü üzere tüm fonksiyonlar doÄal olarak closureâdur ( bunun sadece bir tane istisnası bulunmaktadır bunu "new Function" yazımı bölümünde inceleyebilirsiniz.)
Ãn yüz için bir görüÅmeye gittiÄinizde âClosure nedir?â diye sorulursa doÄru cevap closureâun tanımın verilip tüm JavaScript fonksiyonlarının aslında closure olduÄunun anlatılması ve sonrasında [[Environment]] özelliÄinden, Sözcüksel Ortamdan bahsedilmesi yeterli olacaktır.
Kod blokları ve döngüler, IIFE
Yukarıdaki örnekler fonksiyonlara odaklanmıÅtır. Fakat Sözcüksel Ortam {...} süslü parantez içerisinde de geçerlidir.
Bir kod bloÄu çalıÅtıÄında oluÅturulur ve blok seviyesinde yerel deÄiÅkenleri tutar. AÅaÄıda bir kaç örneÄi bulunmaktadır.
If
AÅaÄıdaki örnekte iÅlem blok çalıÅtıÄında if bloÄunun içine girer, yeni Sözcüksel Ortam âif-onlyâ için yaratılmıÅtır:
Yeni sözcüksel ortam bilgileri dıŠçevreden alabilir, bundan dolayı ifade eriÅilebilirdir. Fakat if içerisindeki tüm deÄiÅkenler ve Fonksiyonel ifadeler kendi Sözcüksel Ortamdan eriÅilebilir, dıÅarıdan eriÅilemez.
ÃrneÄin if bittikten sonra kullanici deÄiÅkeni görünmez olacaktır.
For, while
Her bir döngü kendine ait Sözcüksel Ortama sahiptir. EÄer deÄiÅken for içerisinde tanımlanmıÅsa o sözcüksel ortama yereldir.
for(let i = 0; i < 10; i++) {
// Her döngü kendisine ait sözcüksel ortama sahiptir.
// {i: deger}
}
alert(i); // Hata, böyle bir deÄiÅken yoktur.
Bu aslında istisnadır, çünkü let i, görünürde {...} dıÅındadır. Fakat her döngü kendine ait sözcüksel ortamında iânin o anki deÄerini içermektedir.
Döngüden sonra i görünmez olur.
Kod Blokları
âyalınâ kod bloÄu {...} ile deÄiÅkenler âyerel kapsamaâ tamlanabilir.
ÃrneÄin, bir tarayıcıda tüm kodlar evrensel alanları paylaÅabilir. EÄer bir kod bloÄu içerisinde evrensel alanda bir deÄiÅken yaratılırsa, kodun tamamında kullanılabilir. Fakat bu çatıÅmalara neden olabilir, örneÄin aynı deÄiÅkenler farklı yerlerde yazılabilirler ve birbirlerinin bilgilerini silebilirler.
Bu deÄiÅken isimleri genel kullanılırsa ve kod yazan kiÅi diÄer deÄiÅkenin kullanıldıÄını bilmiyor ise yaÅanılacak bir olaydır.
Bunlardan kaçınmak için bir kod bloÄu oluÅturarak dıÅarıda bulunan evrensel ortamdan izole edilebilir:
{
// yerel deÄiÅkenler ile dıÅarıdaki deÄiÅkenlere etki etmeden istenilen Åekilde izolasyon yapılabilir.
let mesaj = "Merhaba";
alert(mesaj); // Merhaba
}
alert(mesaj); // Hata: mesaj tanımlı deÄildir.
BloÄun dıÅındaki kod içerideki deÄiÅkeni göremez. Ãünkü bir her kod bloÄu kendine ait sözcüksel ortama sahiptir.
IIFE
Eski kodları arasanız âanında çalıÅan fonksiyon ifadeleriâ ( IIFE ) bu amaçla kullanılmıÅtır.
AÅaÄıdaki gibidirler:
(function() {
let mesaj = "Merhaba";
alert(mesaj); // Merhaba
})();
Burada bir fonksiyon ifadesi yaratıldı ve doÄrudan çaÄırıldı. Kod hemen çalıÅır ve kendine ait deÄiÅkenlere sahiptir.
Fonksiyon ifadesi parantez içine alınmıÅtır (function {...}), çünkü eÄer JavaScript ana kod akıÅında "function" görürse bunu Fonksiyon Tanımı olarak algılar. Fakat Fonksiyon Tanımının ismi olmalıdır ve ismi olmadıÄından dolayı bu kod parantez içine alınmaz ise hata verir.
// Error: Unexpected token (
function() { // <-- JavaScript fonksiyon ismini bulamadı. ('i gördü ve hemen hata verdi.
let mesaj = "Merhaba";
alert(mesaj); // Merhaba
}();
âTamam, önemli deÄil, hadi Fonksiyon tanımı yapmak için bir ad verelimâ derseniz bu da çalıÅmaz. Ãünkü JavaScript Fonksiyon Tanımlarının anında çalıÅmasına izin vermez:
// Bu defa aÅaÄıdaki parantez hata verecektir.
function go() {
}(); // <-- Fonskyion Tanımı anında çalıÅtırılamaz.
â¦Bundan dolayı parantez bu fonksiyonun baÅka bir ifade kaynaÄında yaratıldıÄını ifade eder ve bu da Fonksiyon İfadesidir. İsme gerek duymaksızın doÄrudan çalıÅtırılır.
JavaScriptâe baÅka yollarla da Fonksiyon İfadesini belirtmek mümkündür.
// IIFE yaratmanın yolları.
(function() {
alert("Fonksiyon etrafındaki parantezler");
})();
(function() {
alert("Her Åeyin etrafında parantez");
}());
!function() {
alert("Lojik NOT kapısıyla ifadenin baÅlaması.");
}();
+function() {
alert("Matematiksel toplama iÅareti ile ifadenin baÅlaması.");
}();
Yukarıdaki tüm durumlarda Fonksiyon İfadesi tanımlanır ve doÄrudan çalıÅtırılır.
Garbage Koleksiyonu
Sözcüksel Ortam objeleri aynı normal deÄerler gibi hafıza yönetimine konu olurlar.
-
Genelde, Sözcüksel Ortam fonksiyon çalıÅtıktan sonra temizlenir. ÃrneÄin:
function f() { let deger1 = 123; let deger2 = 456; } f();Buradaki iki deÄer teknik olarak Sözcük Ortamının özellikleridir. Fakat
f()bittikten sonra bu Sözcük Ortamı eriÅilemez hale gelir, bundan dolayı hafızadan silinir. -
⦠Fakat
fden sonra hala iç içe fonksiyon var ise[[Environment]]dıÅtaki sözcük ortamını canlı tutar:function f() { let deger = 123; function g() { alert(deger); } return g; } let g = f(); // g ulaÅılabilir ise, dıÅtaki sözcük ortamı canlı kalır. -
EÄer
f()birçok defa çaÄırılırsa ve sonuçları kaydedilirse bu kaydedilen Sözcüksel Ortam objeleri de hafızada kalır. AÅaÄıdaki 3 farklı kodda daha açık bir Åekilde gösterilmiÅtir.function f() { let deger = Math.random(); return function() { alert(deger); }; } // Dizideki 3 fonksiyon da kendine ait sözcüksel ortama sahiptirler. // LE LE LE let arr = [f(), f(), f()]; -
Sözcüksel Ortam objesi eriÅim olmayınca ölür. Bu iç içe fonksiyonların referansı kalmadıÄında meydana gelir. AÅaÄıdaki kodda
geriÅilemez olduÄundavalueâda hafızadan silinir.function f() { let value = 123; function g() { alert(value); } return g; } let g = f(); // g canlı olursa ona karÅılık gelen Sözcüksel Ortam'da hayatta kalır. g = null; // Åimdi hafıza temizlendi.
Gerçek-Hayat Optimizasyonu
GörüldüÄü üzere, teoride bir fonksiyon hayatta olduÄun sürece onun dıÅındaki ona baÄlı deÄiÅkenler de hayatta kalır.
Pratikte ise, JavaScript motoru bunu optimize eder. DeÄiÅken kullanımını analiz eder ve eÄer dıÅarıdaki fonksiyonun kullanılmadıÄı açık ise silinir.
Bunun V8 ( Chrome, Opera)'daki yan etkisi ise böyle deÄiÅkenlerin debugging sırasında da görünememesidir.
AÅaÄıdaki örneÄin Chromeâda konsolu açarak test ediniz.
DurduÄunda konsolda alert(deger) komutunu yazınız.
function f() {
let deger = Math.random();
function g() {
debugger; // konsolda: alert(deger) yazdırın; Böyle bir deÄiÅken bulunamamaktadır.
}
return g;
}
let g = f();
g();
GördüÄünüz gibi böyle bir deÄiÅken bulunamamaktadır. Teoride, eriÅilebilir olmalıdır fakat JavaScript motoru bunu optimize etmiÅtir.
Bu komik debug problemlerine neden olabilir. Bunlardan biri â beklenenin aksine aynı isme sahip dıŠdeÄiÅkenin görülmesi:
let deger = "Sürpriz!";
function f() {
let deger = "En yakın deÄer";
function g() {
debugger; // in console: type alert( value ); Surprise!
}
return g;
}
let g = f();
g();
V8âin bu özelliÄini bilmekte fayda var. EÄer Chrome/Opera ile debugging yapıyorsanız, er ya da geç bu özellikle tanıÅacaksınız.
Bu bir debugger problemi deÄil, V8 motorunun bir özelliÄidir. Belki ileride bu özellik deÄiÅebilir. Bu sayfayadaki örneÄi çalıÅtırarak her zaman bunu kontrol edebilirsiniz.
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)