JavaScript'teki Hoisting Kavramını Anlamak

20 Kasım 2023

JavaScript, bir yorumlayıcı (interpreter) tarafından doğrudan çalıştırılan, derleme işlemine ihtiyaç duymayan bir betik dilidir.

JavaScript'te yazdığımız kodlar yukarıdan aşağı doğru okunur ve yorumlanır. 20 satırlık bir kod yazdığımızda, 1. satır ilk, 20. satır ise en son okunan koddur.

const name = "Burak"; // ilk okunan kod
const surname = "Gür";
 
const fullName = `${name} ${surname}`;
 
const job = "Developer"; // son okunan kod

JavaScript'te Değişken Tanımlama Yöntemleri

JavaScript'te değişken tanımlamak için üç ana yol vardır: var, let ve const. Her biri farklı kapsam (scope) ve yeniden atama kurallarına sahiptir. Kısaca bahsetmek gerekirse;

JavaScript'te, yukarıda da bahsettiğimiz gibi yukarıdan aşağı doğru okunur. Bu yüzden değişken tanımlamalarımızda ilk tanımlayıp ardından kullanırız. Tam tersini yaptığımızda ise hata alırız:

console.log(name); // ReferenceError: Cannot access 'name' before initialization
 
const name = "Burak"; // veya let ile tanımlanabilir

bir istisna dışında:

console.log(name); // undefined
 
var name = "Burak";

Nasıl oldu da const ve let'te ReferenceError hatası verdi de var kullandığımızda undefined yazdı?

Bunu cevabı JavaScript'te hoisting kavramıdır. Hoisting, değişkenlerin ve fonksiyon tanımlarının kendi kapsamının en üstüne "çekilmiş" (hoist) gibi davranmasını ifade eder. Hoisting JavaScript yorumlayıcısının bir kod parçasını çalıştırmadan önce değişken ve fonksiyon tanımlamalarını önceden okuyup kapsamın başına taşıması işlemidir.

Yani var kullandığımızda JavaScript aslında şu şekilde yorumlar:

// Yazılan Kod
console.log(name); // undefined
 
var name = "Burak";
 
 
// Yorumlanma Biçimi*
var name;
 
console.log(name)
 
name = "Burak"

JavaScript motoru bu şekilde kodu yorumlamaz. Sadece daha iyi anlaşılması açısından yazılmıştır.

Aslında let ve const da hoist edilir fakat Temporal Dead Zone (TDZ) nedeniyle ReferenceError verir. Yani JavaScript'in değişkenin varlığını biliyor olmasına rağmen, henüz başlatılmadığı ve kullanıma hazır olmadığı anlamına gelir. Eğer hoisted edilmeseydi şu hatayı alacaktı: ReferenceError: name is not defined onun yerine şu hatayı alıyoruz: ReferenceError: Cannot access 'name' before initialization

Peki ya fonksiyonlar?

Bu durum fonksiyonlar için de geçerlidir. Eğer fonksiyon tanımlanmışsa (function declaration) hoist edilir fakat fonksiyon ifadesiyse (function expression) bu hoist edilmez. Örnek olarak:

console.log(getHi()); // Çalışır ve Hi! döner
 
function getHi() {
    return 'Hi!'}
console.log(getHi()); // TypeError: getHi is not a function
 
var getHi = function() {
    return 'Hi!'}
console.log(getHi()); // TypeError: getHi is not defined
 
const getHi = () => {
    return 'Hi!'}

Sonuç

Hoisting, JavaScript'in çalışma mantığını anlamak için önemlidir. Çünkü bu çalışma mantığını anlamak, beklenmedik hataların ve kafa karışıklıklarının da önüne geçmeye yardımcı olacaktır.

var ile tanımlanan değişkenler, tanımlandıkları kapsamın en üstüne "çekilir" (hoisted). Bu, bazen beklenmeyen hatalara ve karmaşık durumlara yol açabilir. Ancak, let ve const ile tanımlanan değişkenlerde Temporal Dead Zone nedeniyle hata verir, bu da aslında kodun daha anlaşılır ve yönetilebilir olmasını sağlar.