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;
- var: Function scope'dur. Yeniden tanımlanabilir ve değerleri güncellenebilir. Eski JavaScript versiyonlarında kullanılır genellikle.
- let: Block scope'dur. Değerleri güncellenebilir ama yeniden tanımlanamaz. ES6 ile tanıtıldı.
- const Block scope'dur. Değerler güncellenemez ve yeniden tanımlanamaz. ES6 ile tanıtıldı.
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.