JavaScript’te “Hoisting” Kavramı Nedir?
JavaScript’te hoisting kavramı, en kaba tabirle değişken ve fonksiyon tanımlarının kodun üst kısmına taşınması anlamına gelmektedir. Böylece ilgili değişken veya fonksiyon, kodun üst kısmında tanımlanmadığı halde, kodun herhangi bir yerinde kullanılabilirliğine olanak sağlanmış olur.
JavaScript’te “Hoisting” Kavramı
JavaScript’te “hoisting” adını verdiğimiz özellik sayesinde değişkenler ve fonksiyonlar sanki kodun başında tanımlanmış gibi davranır. Yani bir değişkeni ya da fonksiyonu tanımlamadan önce kullanmaya çalışsanız bile, JavaScript sanki onları en başta tanımlamışsınız gibi kabul eder. Haliyle, kodun bazı kısımlarının nerede yazıldığına bakmaksızın çalışması mümkün hale gelir.
“Hoisting” konseptinin bu nevi şahsına münhasır özelliği nedeniyle, kodun daha okunabilir ve organize yazılması teşvik edilir. Zira, bu özelliğin farkında olmadan kod yazmak bazen beklenmeyen sonuçlara yol açabilir. Özellikle “var”, “let” ve “const” kullanımındaki farklılıklar göz önüne alındığında, “hoisting” özelliğinin nasıl çalıştığını anlamak, hataları önlemek ve kodunuzu daha etkili bir şekilde optimize etmek için kritik bir öneme sahiptir.
- İlk olarak fonksiyonlardaki hoisting işlemini ele alalım. JavaScript’te fonksiyonların türü ne olursa olsun fonksiyon çağrıları “fully hoisted” olarak ayarlıdır. Bu sayede kodun neresinde olursa olsun fonksiyon çağrıldığı anda çalışabilir hale gelir. Örneğin:
1 2 3 4 5 |
// Normalde foo(); en alt satırda olmalıydı. foo(); // "Merhaba Dünya!" function foo() { console.log("Merhaba Dünya!"); } |
Yukarıdaki örneği incelediğimizde, foo()
fonksiyonu, önce çağrılmış daha sonra tanımlanmıştır. Ancak “hoisting” sayesinde, fonksiyonun tanımı kodun üst kısmına taşındığı için, herhangi bir kod hatası olmadan çalışmış ve “Merhaba Dünya!” çıktısını vermiştir.
Ancak belirtmek isterim ki, “function expression”, yani fonksiyonu bir değişkene atama işlemi “fully hoisted” değildir. Zira “function expression” hoisted olurken sadece değişkenin tanımı yukarı taşınır, fonksiyonun kendisi değil. Eğer bu değişkeni fonksiyonun tanımlandığı satırdan önce çağırırsanız, bu değişkenin değeri “undefined” olacaktır. Aşağıdaki örneği inceleyiniz:
1 2 3 4 5 6 7 |
console.log(myFunction); // undefined var myFunction = function() { return "Merhaba!"; } console.log(myFunction()); // Merhaba! |
İlk console.log
‘da, myFunction
adlı değişken tanımlı ancak ona bir değer atanmamış olduğu için “undefined” olarak loglanır. İkinci console.log
‘da ise değişkene fonksiyon atanmış ve fonksiyon çağrıldığı için “Merhaba!” mesajı loglanır.
Sıra geldi, değişken atamak için kullandığımız anahtar kelime olan var, let ve const anahtar kelimeleriyle “hoisting” nasıl oluyormuş onu göstermeye. JavaScript’te “var”, “let” ve “const” anahtar kelimeleri, “hoisting” kavramına farklı şekillerde tepki verirler.
İlk olarak şunu belirtmek istiyorum; “hoisting” ile değişkenlerin kendileri yukarı taşınsa da sahip oldukları değerler taşınmaz.
- JavaScript’te
var
anahtar kelimesiyle tanımlanan değişkenler, başlangıçta otomatik olarakundefined
değerine sahiptir. Tahmin edebileceğiniz üzere bu durum, değişken için bellekte yer ayrıldığını, ancak henüz bir değer atanmadığını anlatır. Eğer bu değişkene değer atanmamışsa, değer atanana kadarundefined
olarak kalacaktır. Örneğin:
1 2 3 4 5 6 |
console.log(ornekDegisken); // undefined var ornekDegisken; // tanımlama yapıldı, değer atanmadı console.log(ornekDegisken); // undefined ornekDegisken = "Merhaba!"; console.log(ornekDegisken); // Merhaba! |
Yukarıdaki kodumuzda, ornekDegisken
adlı değişkeni tanımladık ama başlangıçta bir değer atamadık, bu yüzden ilk loglama işleminde undefined
çıktısını aldık. Daha sonra bir değer atadık ve hemen ardına yazdığımız ikinci loglama işleminde çıktıyı aldık.
let
ve const
anahtar kelimeleri, ES6 ile birlikte JavaScript’e eklenmiş olan yeni değişken tanımlama yöntemleridir. Her birinin kendine has kullanım alanı olsa da biz bu yazımızda var
ile aralarındaki “hoisting” davranış farkını inceleyeceğiz.
let
ile tanımlanan değişkenler de tıpkıvar
anahtar kelimesinde olduğu gibi “hoisting” mekanizması tarafından kodun başına taşınırlar. Ancak farklı olarak, bu değişkenlere otomatik olarakundefined
değeri atanmaz. Bu nedenle, bir değişkenilet
ile tanımladıktan önce erişmeye çalışırsanız, birReferenceError
hatası ile karşılaşırsınız. Örneğin:
1 2 3 4 5 6 |
console.log(ornekDegisken); // Uncaught ReferenceError: Cannot access 'ornekDegisken' before initialization let ornekDegisken; // tanımlama yapıldı, değer atanmadı console.log(ornekDegisken); // undefined ornekDegisken = "Merhaba!"; console.log(ornekDegisken); // Merhaba! |
Yukarıdaki kodda, başlangıçta ornekDegisken
‘in değerini ekrana yazdırmaya çalışıyoruz. Fakat bu noktada değişken henüz tanımlanmadığı için “Cannot access ‘ornekDegisken’ before initialization” şeklinde bir hata mesajı alıyoruz. Bu hatayı düzeltmek için ilgili satırı kaldırdığımızda, kodun devamında ornekDegisken
‘i let
anahtar kelimesiyle tanımlıyoruz ama herhangi bir değer atamadık. Böyle durumlarda, ilgili değişkenin başlangıç değeri otomatik olarak undefined
olarak belirlenir. Kodun devamında ornekDegisken
‘e “Merhaba!” değerini atadığımızda ise değişkenin değerini ekrana yazdırdığımızda “Merhaba!” çıktısını vermektedir.
const
da benzer şekilde hoisting edilir. Ancak yinelet
gibi, tanımlanan değişkene otomatik olarak bir başlangıç değeri atanmaz. Ek olarak,const
ile bir değişken tanımlarken bu değişkene başlangıçta bir değer atamak zorunludur. Çünküconst
, sadece bir kere atanabilen ve sonrasında değiştirilemeyen bir değişkeni ifade eder.
1 2 3 4 5 6 |
console.log(ornekSabiti); // Uncaught ReferenceError: Cannot access 'ornekSabiti' before initialization const ornekSabiti; // SyntaxError: Missing initializer in const declaration console.log(ornekSabiti); // Bu satıra kod asla ulaşamaz const ornekSabiti = "Merhaba!"; console.log(ornekSabiti); // Merhaba! |
Yukarıdaki kodda, ilk olarak ornekSabiti
adında bir sabiti tanımlamadan kullanmaya çalışıyoruz ve hata alıyoruz. Ardından bu sabiti tanımlamaya çalışıyoruz fakat const
ile tanımlanan bir değişkene başlangıçta değer atanmadığı için hata alıyoruz. Sonrasında ise örnekSabiti
değişkenini tanımlarken değer atamasını yapıyoruz. Haliyle değerimiz ekrana yazdırılıyor.
Özetle, eğer bir değişken, var
anahtar kelimesiyle tanımlanmışsa ve bu değişken “hoisted” oluyorsa, o değişkenin başlangıç değeri undefined
olarak atanır. Ancak let
ve const
ile tanımlanan değişkenler “hoisted” olduğunda bile erişilemez durumda kalır ve erişilmeye çalışıldığında hata alınır.
❗ Hoisting ile her ne kadar kodun daha modüler bir hale gelmesi kolaylaşıp kodun çalışma hızı artsa da mümkün olduğunca hoisting’ten kaçınmak gerekir. Özellikle değişken bildirimlerinde var
yerine let
veya const
tercih edilmelidir. Zira kodunuzda bellek sızıntıları (memory leak) ve/veya yakalanması zor hatalar ortaya çıkabilir.