Menu

JavaScript'te let, const ve var Farkı: Scope ve Hoisting

JavaScript'te değişken tanımlamanın üç yolu var. Modern kodda neden varsayılan olarak const, gerektiğinde let ve neredeyse hiç var kullanmıyoruz?

Üç Farklı Anahtar Kelime, Tek Bir İş

JavaScript'te değişken tanımlamanın üç yolu var: var, let ve const. Üçü de bir isme bir değer bağlıyor ama kapsam (scope), yeniden atama kuralları ve tanımlanmadan önceki davranışları açısından birbirinden ayrılıyor. Modern JavaScript kodunda genelde const tercih edilir; değer değişecekse let kullanılır, var ise neredeyse hiç kullanılmaz.

Kısacası şöyle:

index.js
Output
Click Run to see the output here.

Bu sayfanın geri kalanında, o üç satırın neden böyle davrandığını anlatacağız.

const: Varsayılan tercih

const, yeniden atanamayan bir bağlama oluşturur. Bir kez const x = 5 dediğinizde, sonradan x = 6 diyemezsiniz — motor size bir TypeError fırlatır.

index.js
Output
Click Run to see the output here.

Kural aslında bu kadar basit. İyi yazılmış bir programdaki değişkenlerin çoğuna zaten yeniden değer atanmasına gerek kalmadığı için const durumların büyük bölümüne uyar. Önce const kullanma alışkanlığı, değerin gerçekten değiştiği yerleri de göze çarpar hale getirir.

Burada sık karşılaşılan bir kafa karışıklığı var: const değerin kendisini değil, bağlamayı (binding) korur. Değer bir nesne ya da dizi ise içeriği hâlâ değiştirilebilir:

index.js
Output
Click Run to see the output here.

const, "bu isim her zaman bu nesneyi gösterir" anlamına gelir. "Bu nesne asla değişmez" demek değildir. Nesnenin kendisini dondurmak istiyorsan Object.freeze(user) tam da bunun için var — ama pratikte çoğu kod, const ile tanımlanan nesnelerin değiştirilmemesi gerektiği yönündeki yazılı olmayan kurala güvenir.

let: Yeniden Atama Gerektiğinde

let, yeniden atama yapabilmen dışında her konuda const ile aynıdır. Sayaçlar, toplam tutan değişkenler, döngü değişkenleri ve değerin zaman içinde gerçekten değiştiği her yerde let kullan.

index.js
Output
Click Run to see the output here.

Kendinizi let ile tanımladığınız bir değişkeni hiçbir zaman yeniden atamadığınızı fark ediyorsanız, onu const yapın. Çoğu projedeki linter zaten sizi bu yönde uyaracaktır.

Block Scope (Blok Kapsamı)

Hem let hem de const blok kapsamlıdır. Blok dediğimiz şey, { ile } arasında kalan her şeydir: bir if'in gövdesi, bir for döngüsü, bir fonksiyon, hatta tek başına duran { ... } blokları bile. Bir blok içinde tanımlanan değişken, o bloğun dışında yoktur.

index.js
Output
Click Run to see the output here.

Tam da istediğiniz davranış bu. Değişkenler ait oldukları işin içinde kalır ve isim çakışmaları imkânsız hale gelir.

var bunu yapmaz — zaten var kullanmamanızın baş sebebi de bu.

var: Fonksiyon Kapsamı ve Sürprizler

var, en yakın fonksiyona göre kapsamlanır; en yakın bloğa göre değil. Yani if ya da for içinde tanımlanan bir var, dışarıdaki fonksiyona sızar:

index.js
Output
Click Run to see the output here.

Bu gevşek kapsam, JavaScript'in klasik hatalarının uzun bir listesinin kaynağı aslında — en meşhuru da döngülerde her iterasyonun aynı var i'yi paylaşması ve tüm callback'lerin sonunda aynı son değeri görmesi durumu.

Ayrıca var ile aynı kapsamda aynı ismi tekrar tekrar tanımlayabilirsiniz; hiç şikayet etmez. Bu da yazım hatalarını gözden kaçırmanıza yol açar:

index.js
Output
Click Run to see the output here.

Modern JavaScript kodlarında artık neredeyse sadece let ve const kullanılıyor. var ise daha çok eski projelerde, yıllanmış Stack Overflow cevaplarında ve çok eski tarayıcılarda çalışması gereken script'lerde karşımıza çıkıyor.

Hoisting ve Temporal Dead Zone

Üç tanımlama da hoist edilir — yani motor, blok içindeki kodu çalıştırmadan önce bu değişkenlerden haberdardır — ama tanımlama satırına gelinmeden önceki davranışları birbirinden farklıdır.

var hoist edilir ve undefined değeriyle başlatılır. Yani var satırından önce değişkene erişmeye çalışsan bile hata almazsın:

index.js
Output
Click Run to see the output here.

let ve const da aslında hoist edilir ama başlangıç değeri atanmaz. Tanım satırına gelmeden bu değişkenlere dokunmaya çalışırsan hata alırsın. Scope'a girdiğin an ile tanım satırının çalıştığı an arasındaki bu boşluğa temporal dead zone (TDZ) deniyor:

index.js
Output
Click Run to see the output here.

TDZ bir bug değil, bilinçli bir tasarım tercihi. "Tanımlanmadan önce kullanıldı" durumunu sessiz sedasız bir undefined olmaktan çıkarıp gürültülü bir hataya dönüştürür; bu da bir sürü yazım hatasını ve sıralama hatasını kolayca yakalamanı sağlar.

for...of Döngüsünde const Kullanımı

Küçük ama sık karşılaşılan bir durum: for...of döngüleri her iterasyonda yeni bir binding oluşturur. Dolayısıyla döngü değişkeni iterasyonlar arasında "değişiyor" gibi görünse de rahatlıkla const kullanabilirsin.

index.js
Output
Click Run to see the output here.

Döngünün her turunda name kendi bağlamasına sahip oluyor; yani tek bir değişken tekrar tekrar atanmıyor. Klasik for (let i = 0; i < n; i++) yazımında ise let şart, çünkü i her iterasyonda artırılan tek bir bağlamadır.

Pratik Bir Kural

Değişken tanımlarken şu sıralamayı takip edin:

  • Varsayılan olarak const kullanın. Değer yeniden atanmayacaksa bunu açıkça belirtmiş olursunuz.
  • Bağlamanın gerçekten değişmesi gerekiyorsa let tercih edin.
  • var'ı yalnızca onu zorunlu kılan eski bir kod tabanında çalışıyorsanız kullanın.

Bu yaklaşımı tutarlı bir şekilde uyguladığınızda, her değişkenin niyeti ilk bakışta anlaşılır olur: yeniden atanabilir mi değil mi, bu bloğa mı yoksa bu fonksiyona mı ait.

index.js
Output
Click Run to see the output here.

MAX_RETRIES ve users hiç değişmiyor — const. successful zaman içinde artıyor — let. user ise her iterasyonda sıfırdan oluşan bir bağlama — const. Kodu yukarıdan aşağıya okurken, çalıştırmana gerek kalmadan hangi değerlerin hareket ettiğini, hangilerinin sabit kaldığını rahatça görebilirsin.

Sırada: Primitive Tipler

Artık değişken tanımlamayı bildiğine göre, sıradaki soru şu: bu değişkenler ne tür değerler tutabilir? JavaScript'te küçük ama önemli bir primitive tip kümesi var — sayılar, string'ler, boolean'lar ve birkaç tane daha — ve her birinin kendine has davranışları var. Bir sonraki sayfada bunlara bakacağız.

Sıkça Sorulan Sorular

JavaScript'te let, const ve var arasındaki fark nedir?

const ve let ES2015 ile geldi ve block-scoped'dır; var ise daha eski ve function-scoped çalışır. const ile tanımlanan bir binding'e yeniden değer atayamazsınız, let ile atayabilirsiniz. Ayrıca var hoist edilir ve undefined olarak başlatılır; let ve const de hoist edilir ama tanımlama satırı çalışana kadar erişilemez — buna temporal dead zone deniyor.

JavaScript'te const değeri gerçekten immutable yapar mı?

Hayır. const yalnızca binding'in yeniden atanmasını engeller. Değer bir object ya da array ise içeriğini hâlâ değiştirebilirsiniz — const user = {}; user.name = 'Ada' gayet çalışır. Gerçek anlamda değişmezlik istiyorsanız Object.freeze kullanın veya bu iş için bir kütüphaneye bakın.

Modern JavaScript'te hâlâ var kullanmalı mıyım?

Neredeyse hiç. let ve const hem daha net scope kurallarına sahip hem de birçok bug'ı parse aşamasında yakalıyor. var ile karşılaşacağınız tek yer eski kod tabanları ve bazen ES2015 desteği olmayan ortamlarda çalışması gereken birkaç script olur.

Coddy ile kodlamayı öğren

BAŞLA