Üç 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:
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.
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:
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.
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.
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:
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:
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:
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:
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.
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
constkullanın. Değer yeniden atanmayacaksa bunu açıkça belirtmiş olursunuz. - Bağlamanın gerçekten değişmesi gerekiyorsa
lettercih 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.
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.