İçeriğe geç
academia.sh

Ders 04 / 21

Değişken Bildirimleri

Fonksiyon kapsamlı ve blok kapsamlı bildirimlerin farkı, yeniden bildirim kuralları, sabit bağlamanın kapsamı ve döngü değişkeninin bağlanması.

İçindekiler

Önceki ders, yeni yeteneklerin eskisinin yerine değil yanına eklendiğini söyledi. Bunun en somut örneği bildirim türleridir: dilin ilk halinde tek bir bildirim vardı, ES2015 ile iki bildirim daha eklendi ve eskisi kaldırılmadı. Üçü aynı işi yapmaz.

Programlama Temelleri kursundaki Kapsam ve Yaşam Süresi dersinde kapsam, bir adın görüldüğü program bölgesi olarak tanımlanmıştı. Bu ders o tanımı JavaScript’e uygular ve üç bildirimin hangi bölgeyi seçtiğini gösterir. Fark yazım tercihi değildir; adın nerede görüleceğini, ne zaman erişilebilir olacağını ve yeniden bağlanıp bağlanamayacağını belirler.

İki Kapsam Birimi

var ile bildirilen ad fonksiyon kapsamlıdır: içinde bulunduğu fonksiyonun tamamında görülür. Bildirimin bir blok içinde yapılmış olması bir şeyi değiştirmez.

let ve const ile bildirilen ad blok kapsamlıdır: yalnızca içinde bulunduğu süslü ayraç çiftinde ve onun içindeki bloklarda görülür.

function kapsamDenemesi() {
  if (true) {
    var varDegisken = "var ile";
    let letDegisken = "let ile";
    console.log(varDegisken, "/", letDegisken);
  }
  console.log(varDegisken);
  console.log(typeof letDegisken);
}
kapsamDenemesi();
var ile / let ile
var ile
undefined

Blok kapandıktan sonra var ile bildirilen ad hâlâ erişilebilir; let ile bildirilen ad görünmez olmuştur. Üçüncü satırın typeof ile yazılmasının nedeni budur: doğrudan okumak hata verirdi.

Hatanın kendisini görmek, ayrımın keskinliğini gösterir:

if (true) {
  let esik = 20;
  console.log(esik);
}
try {
  console.log(esik);
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
20
ReferenceError: esik is not defined

Blok kapsamı, adın ömrünü ihtiyaç duyulan bölgeyle sınırlar. Programlama Temelleri kursundaki gölgeleme ve ad çakışması tartışması burada doğrudan işe yarar: bir adın görünürlük alanı ne kadar darsa, kazara ezilme olasılığı o kadar azdır.

Yeniden Bildirim

Üç bildirim, aynı adın ikinci kez bildirilmesine farklı tepki verir.

var, aynı kapsamda aynı adın yeniden bildirilmesine izin verir. İkinci bildirim yeni bir ad yaratmaz; var olan ada yeni bir değer atar:

var esik = 20;
var esik = 25;
console.log(esik);
25

Bu esneklik, uzun bir fonksiyonun ortasında farkında olmadan aynı adı ikinci kez kullanmayı sessiz bir hataya çevirir. let ve const aynı kapsamda yeniden bildirimi reddeder:

let esik = 20;
let esik = 25;

Bu dosya çalıştırıldığında hiçbir satırı yürütülmez:

SyntaxError: Identifier 'esik' has already been declared

Hatanın türüne dikkat edin. Bu bir çalışma zamanı hatası değil, ayrıştırma hatasıdır; ilk derste kurulan ayrım burada işe yarıyor. Aynı adın iki kez bildirilmesi, kaynağın tamamı okunurken saptanabilir bir durumdur, bu yüzden program hiç başlamaz.

const bildirimi ayrıca başlangıç değeri ister:

const esik;
SyntaxError: Missing initializer in const declaration

Bunun nedeni sonraki bölümde görülecek: sabit bir ad yalnızca bir kez bağlanabildiği için, bağlanmamış bir sabit hiçbir zaman değer alamazdı.

Sabit Bildirimin Sınırı

const, bağlamayı sabitler. Programlama Temelleri kursundaki Değişkenler ve Bağlama dersinde ad ile değer arasındaki ilişki “bağlama” olarak adlandırılmıştı; const tam olarak o ilişkinin değişmesini engeller.

const esik = 20;
try {
  esik = 21;
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
TypeError: Assignment to constant variable.

Sabitlenen şey bağlamadır, değerin içeriği değil. Bağlanan değer değiştirilebilir bir nesneyse, nesnenin içeriği değişebilir:

const kayit = { istasyon: "A1", sicaklik: 21.5 };
kayit.sicaklik = 22;
console.log(kayit);
{ istasyon: 'A1', sicaklik: 22 }

Ad hâlâ aynı nesneye bağlıdır; değişen, nesnenin bir özelliğidir. Aynı ada başka bir nesne bağlamak ise reddedilir:

const kayit = { istasyon: "A1", sicaklik: 21.5 };
try {
  kayit = { istasyon: "A2", sicaklik: 19 };
} catch (hata) {
  console.log(hata.name + ": " + hata.message);
}
TypeError: Assignment to constant variable.

Bu ayrım terminolojiyle örtüşür: const bir sabit (constant) bağlama kurar, değeri değiştirilemez (immutable) yapmaz. Nesnelerin içeriğini gerçekten dondurmanın yolu ayrı bir işlemdir ve bu kursun kapsamı dışındadır.

Döngü Değişkeninin Bağlanması

Kapsam farkının en çok sonuç doğurduğu yer döngü başlığıdır. var ile bildirilen döngü değişkeni tüm döngü boyunca tek bir bağlamadır; let ile bildirilen değişken her turda yeniden bağlanır.

Fark, döngü içinde üretilen fonksiyonlarda görünür:

const varIsleri = [];
for (var i = 0; i < 3; i++) {
  varIsleri.push(() => i);
}
console.log(varIsleri.map((f) => f()));

const letIsleri = [];
for (let j = 0; j < 3; j++) {
  letIsleri.push(() => j);
}
console.log(letIsleri.map((f) => f()));
[ 3, 3, 3 ]
[ 0, 1, 2 ]

İlk döngüde üç fonksiyon da aynı i bağlamasını okur. Döngü bittiğinde o bağlamanın değeri 3’tür — koşul bu değerde yanlışa döndüğü için döngü sonlanmıştır. Fonksiyonlar daha sonra çağrıldığında hepsi 3 görür.

İkinci döngüde her tur için ayrı bir j bağlaması oluşur ve her fonksiyon kendi turunun bağlamasını okur. Bu davranış let bildiriminin döngü başlığına özgü bir kuralıdır: tur başında yeni bağlama yaratılır ve önceki turun değeriyle başlatılır.

Ok fonksiyonu sözdizimi (() => i) burada yalnızca kısa yazım için kullanıldı; davranışı Ok Fonksiyonları dersinde ele alınacak. Fonksiyonun kendisini yaratıldığı kapsamdaki adlara bağlı tutmasına kapanış (closure) denir; Programlama Temelleri kursunda tanımlanmıştı ve bir sonraki kursta ayrıntılanacaktır.

Koleksiyon üzerinde dönerken const kullanılabilir, çünkü her tur yeni bir bağlama kurar ve tur içinde değişken yeniden atanmaz:

const sicakliklar = [21.5, 19.75, 23];
for (const deger of sicakliklar) {
  console.log(deger);
}
21.5
19.75
23

Ölçüm Betiğine Uygulama

Kursun ekseni olan betik, bu kurallara göre yeniden yazıldığında hangi adın neden hangi bildirimle verildiği açıklanabilir hale gelir:

const sicakliklar = [21.5, 19.75, 23, 18.25];
const esik = 20;

let toplam = 0;
let esikUstu = 0;

for (const deger of sicakliklar) {
  toplam += deger;
  if (deger > esik) {
    const fark = deger - esik;
    esikUstu += 1;
    console.log("esik asildi, fark:", fark);
  }
}

console.log("ortalama:", toplam / sicakliklar.length);
console.log("esik ustu okuma:", esikUstu);
esik asildi, fark: 1.5
esik asildi, fark: 3
ortalama: 20.625
esik ustu okuma: 2

Üç karar birlikte alındı. Yeniden bağlanmayacak adlar (sicakliklar, esik, tur değişkeni deger, blok içi fark) const ile bildirildi. Yalnızca birikeç görevindeki iki ad (toplam, esikUstu) let aldı. fark adı yalnızca if bloğunda gerektiği için orada bildirildi; blok kapsamı sayesinde döngünün geri kalanında görünmez.

Bu yazım kuralı kurs boyunca korunacak: yeniden bağlanmayan her ad const ile, yeniden bağlanan ad let ile bildirilir. var yalnızca davranışı gösterilirken kullanılacak. Gerekçe estetik değildir — bir adın yeniden bağlanıp bağlanmadığı bildirimden okunduğunda, kodu izlemek için tüm gövdeyi taramak gerekmez.

Özet

  • var fonksiyon kapsamlıdır; let ve const blok kapsamlıdır ve süslü ayraç çiftinin dışında görünmez.
  • Aynı kapsamda var yeniden bildirilebilir; let ve const yeniden bildirimi ayrıştırma aşamasında SyntaxError ile reddeder.
  • const bağlamayı sabitler, değeri değiştirilemez yapmaz: nesnenin özellikleri değişebilir, ada başka nesne bağlanamaz.
  • var ile bildirilen döngü değişkeni tek bir bağlamadır; let her turda yeni bağlama kurar ve döngü içinde üretilen fonksiyonlar bu farkı gösterir.
  • Kurs kuralı: yeniden bağlanmayan ad const, birikeç görevindeki ad let alır.

Sonraki Adım

Kapsam sorusunun yarısı yanıtlandı: bir adın nerede görüldüğü. Diğer yarısı ne zaman erişilebilir olduğudur. Bildirimden önceki satırda aynı ada dokunmak var ile undefined verirken let ile hata üretir; ikisi de bildirimin “yukarı taşındığı” gerçeğiyle açıklanır. Sonraki ders sözcüksel kapsamı, yukarı taşımayı ve bildirim öncesi bölgenin kurallarını ele alacak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat