Programlama

JavaScript’te Kod Nasıl Çalışır: Context ve Scope

JavaScript’e yeni girenlerin en çok tökezlediği yerlerden biri şu: Kod satır satır akıyor gibi görünür, ama perde arkasında işler epey farklı döner. Ben de 2019 yazında, İstanbul’da bir e-ticaret paneli üstünde çalışırken bunu acı şekilde öğrendim. Bir değişkenin neden “durduk yere” erişilebilir olduğunu anlayamadığım o gün — saatlerce baktım ekrana, olayın köküne inmem gerektiğini. O zaman fark ettim.

İşin aslı şu ki, JavaScript yalnızca “çalıştırıp geçmez”. Önce ortamı hazırlar. Hangi değişken nerede duruyor, bu fonksiyon içinde this neye bakıyor, hangi değerler dışarıdan sızabiliyor… Bunların tamamı ayrı bir düzenin parçası. Açık konuşayım, bu düzeni kavramadan kod yazmak biraz karanlıkta kablo döşemeye benziyor — belki çalışır, belki çalışmaz, neden çalışmadığını hiç bilemezsiniz.

💡 Bilgi: Bu yazıda ES5 mantığını temel alıyorum. ES6+ tarafında LexicalEnvironment ile VariableEnvironment arasındaki ilişki biraz daha farklı ele alınıyor. Yani burada anlatılanlar sağlam bir temel veriyor ama modern JavaScript’te bazı ayrıntılar değişiyor.

Execution Context Nedir, Neden Umursamalısınız?

Execution context’i kaba haliyle “JavaScript’in o anki çalışma odası” gibi düşünebilirsiniz. İçinde değişkenler var, fonksiyonlar var, this var. Yani kodun nefes aldığı alan bu. Ben bunu ilk kez gerçekten kavradığımda yıl 2021’di — Ankara’da küçük bir SaaS projesinde, aynı isimli iki değişken yüzünden çıkan saçma bir bug’ı çözerken anladım ki sorun kodda değil, benim zihnimde; meseleyi yanlış modelliyordum.

Bir dakika — bununla bitmedi.

Şunu söyleyeyim, Peki neden bu kadar önemli? Bazı şeyler ancak bu çerçevede anlam kazanıyor. Tanımlanmamış gibi görünen bir değişkene erişebilmeniz, ya da bir fonksiyon içinden dıştaki veriyi rahatça okuyabilmeniz (ki bu çoğu kişinin gözünden kaçıyor). Bunlar rastgele değil. JavaScript engine ortada görünmeyen bir yapı kuruyor ve kodu onun üzerinden yürütüyor. Siz farkında olmasanız da bu yapı orada, sessiz sedasız çalışıyor.

İşin garibi, Bir de işin psikolojik tarafı var — bence en sinir bozucu kısım da bu zaten. Kodunuz çalışıyor olabilir. Neden çalıştığını bilmiyorsanız, ufak bir değişiklikte sistem domino taşı gibi devrilebiliyor. En çok da büyük projelerde, her şey kağıt üstünde doğru görünürken pratikte bambaşka davranan bir sistem karşısında oturup “ne olduğunu” anlayamamak gerçekten yıpratıcı.

Koddan önce hazırlık

Execution context oluştuğunda engine önce etrafı bir yokluyor diyelim. Hangi isimler var, hangileri sonradan değişebilir, this neyi gösterecek… Bunları ayarlıyor, sonra yürütmeye geçiyor. Yani “önce sahne kuruluyor, sonra oyun başlıyor” mantığı var — bu cümleyi okuduğumda aklıma hep tiyatro gelir, klişe biliyorum ama gerçekten öyle.

Mutfak benzetmesiyle düşünün: Malzemeleri doğramadan direkt tavaya atmak gibi bir şey bu (ki bu çoğu kişinin gözünden kaçıyor). Olur da bir şekilde yemek pişer ama sonuç pek iç açıcı olmaz. JavaScript de öyle; önce bağlam kurulur, sonra işlem başlar. Sırayı atlamak istemezsiniz.

Bunu biraz açayım.

Global Context ve Function Context Arasındaki Fark

Bi saniye — JavaScript ilk açıldığında global execution context devreye girer. Sayfa yüklenince ya da script ayağa kalkınca orası aktif olur ve script bitene kadar yaşar. Sonra her fonksiyon çağrısı için yeni bir function execution context oluşturulur; işini görür ve sahneden çekilir.

Bunu 2022’de Berlin’de katıldığım küçük bir frontend workshop’unda canlı canlı göstermişlerdi. Eğitmen basit iki fonksiyon yazdı; biri diğerini çağırıyordu. Hepimiz ekrana bakıp “tamam işte sıra böyle gidiyor” dedik ama call stack çizimi gelince işin rengi değişti. Meğer görünmeyen asıl hikâye orada dönüyormuş. O ana kadar sadece yüzeyi görüyormuşuz.

function second() {
console.log('second');
}
function first() {
second();
}
first();

Küçük startup senaryosu: Tek sayfalık uygulamada global scope’a fazla güvenebilirsiniz,. Proje büyüdükçe işler karışır. Herkes aynı yere değişken bırakmaya başlarsa isim çakışması kaçınılmaz — bu kadar.

Kurumsal proje senaryosu: Modüler yapı şarttır. Fonksiyon context’lerinin nasıl oluştuğunu bilmek, debug süresini ciddi biçimde düşürür; çünkü “nerede hangi veri yaşar” sorusunun cevabı netleşir. Saatlerce süren kör arama seanslarından kurtulursunuz.

Call stack işi nasıl yürütüyor?

Call stack’i üst üste dizilen tabaklar gibi düşünün. En üstte hangi context varsa aktif olan odur. Yeni fonksiyon çağrıldığında o da tepeye eklenir; işi bitince çıkar gider. Basit ama etkili bir mekanizma — ve bir kez kafanızda oturursa artık çok şey yerine oturuyor. Yazılımı Yapay Zekâya Bırakmak: Çok Ajanlı Sistemler yazımızda bu konuya da değinmiştik.

Aşama Neler oluyor? Kodun durumu
1 Global context oluşuyor Sistem hazırlanıyor
2 first() çağrılıyor Kendi context’i yığına ekleniyor
3 second() çağrılıyor Daha üstte yeni context açılıyor
4 second() bitiyor Tepedeki context çıkıyor
5 first() bitiyor Sıra geri geliyor

X Ray Gibi Çalışan İki Yapı: VE ve LE

İnanın, Anlatması biraz teknik gelebilir. Mesele aslında şu: VariableEnvironment başlangıçta ne varsa onu tutuyor; LexicalEnvironment ise çalışma sırasında güncellenen, daha “canlı” taraf gibi davranıyor. İlk anda ikisi aynı görünüyor olabilir. Zamanla yollar ayrılıyor.

Açık konuşayım, ben bu farkı ilk okuduğumda hafif afalladım. 2020’nin Kasım ayında İzmir’de eski bir projeyi refactor ederken hoisting kaynaklı garipliklerle boğuşurken bu ayrımın ne kadar kritik olduğunu gördüm — o zamana kadar “bunlar neredeyse aynı şey değil mi?” diye düşünüyordum. Değilmiş.

Execution context’i anlamanın en iyi yolu teoriyi ezberlemek değil; küçük örnekler üzerinde call stack’i ve scope zincirini gözünüzde canlandırmak.

LexicalEnvironment daha “yaşayan” taraftır diyebiliriz. Kodu çalıştırdıkça bazı bağlamlar güncellenir, yeni referanslar ortaya çıkar. VariableEnvironment ise daha çok başlangıç notu gibi kalır — değişmez, sabit, ilk andaki durumu yansıtır.

  • User-friendly taraf: Kodu izlemek kolaylaşır.
  • Zor taraf: İlk öğrenmede kafa karıştırabilir.
  • Dikkat edilmesi gereken: ES6 sonrası detaylar farklıdır. — bunu es geçmeyin

ThisBinding Neden Bazen Sapıtıyor?

ThisBinding, yani this‘in kimi gösterdiği meselesi ayrı bir dert. Bağlama göre şekil değiştirir. Aynı kelime başka yerde başka şeyi işaret edebilir ve tam burada insanlar genelde duvara tosluyor — “ben doğru yazdım ya, neden bu nesneyi göstermiyor?” sorusuyla saatlerce oturanları gördüm (bu beni çok şaşırttı) Bu konuyla ilgili Cebinizde Kalan Zihin Günlüğü: Tarayıcıda Offline Yapay Zekâ yazımıza da göz atmanızı tavsiye ederim.

Bunu geçen yıl Şubat ayında İstanbul’daki bir fintech ekibinde test ettiğimde bizzat gördüm: Normal fonksiyon ile arrow function arasındaki fark yüzünden aynı bileşen iki farklı davranış sergiliyordu ve evet, ekipten biri haklı olarak sinirlenmişti (şaşırtıcı ama gerçek). Sorun aslında syntax değilmiş; bağlam okunmadan yazılmış kodmuş. İki satır fark, saatlik toplantı.

İşte tam da bu noktada devreye giriyor. Dosya Paylaşımı Güvenli mi? 2026’da Bilmeniz Gerekenler yazımızda bu konuya da değinmiştik.

Lafı gevelemeden söyleyeyim: this‘i ezberle çözemezsiniz. Konteksti okuyarak kontrol edersiniz. Bir kere mantığını oturtunca mesele bayağı sadeleşiyor… ama öncesinde biraz süründürür, bunu da söyleyeyim.

Nerede işe yarar?

Yani, Sınıf yapılarıyla çalışan uygulamalarda ya da event handler içinde ThisBinding‘i doğru anlamak ciddi avantaj sağlıyor (ben de ilk duyduğumda şaşırmıştım). Aksi halde data yanlış nesneyi işaret eder ve hata zinciri uzar gider. Küçük projede “idare eder” diyebilirsiniz. Ama enterprise seviyede aynı yaklaşım gecenin üçünde alarm olarak geri döner — bu deneyimi yaşayanlar bilir ne demek istediğimi (ciddiyim)

Peki Scope Chain Ne İşe Yarıyor?

Küçük bir detay: Scope chain diye bir kavram var diye ürkmeyin; mesele aslında isim çözümleme zinciri. JavaScript mevcut scope içinde aradığı ismi bulamazsa yukarı doğru çıkar. Önce kendi cebine bakar. Yoksa yan cebe geçer.

İşte, ben bunu ilk kez düzgün kavradığımda tarih 2023 Mart’tı; Kadıköy’de freelance çalışırken nested function yapısı içindeki dış değişkene erişim olayını debug ediyordum. O gün anladım ki scope chain aslında haritada yol bulmak gibi: sokakta değilsinizdir ama semtin adını bilirseniz eve ulaşırsınız. Basit ama yerinde bir benzetme bence.

Şimdi gelelim pratik tarafa:

  • En yakın scope önce kontrol edilir
  • Bulunamazsa dış scope’a çıkılır
  • En sonunda global’e kadar gidilir
  • Hiçbir yerde yoksa hata patlayabilir

Güzel tarafı şu: Karmaşayı azaltıyor. Kötü tarafı ise — ve bu insanları en çok yoruyor — yanlış yerden veri çekerseniz sessiz sessiz bug üretiyor. Hata mesajı yok, uyarı yok; sadece yanlış sonuç. Bir de şunu ekleyeyim: Scope zinciri doğru anlaşılmazsa closure mantığı da havada kalır — bence çok yerinde bir karar —. Yani konu sadece teorik değil, debug sürenizi doğrudan etkiliyor. Android’den Kaldırılan DDLC: Bir Oyunun İnce Çizgisi yazımızda da bu konuya değinmiştik. Anthropic’in Çifte Şoku: Mythos, Para ve Güvenlik Dengesi yazımızda da bu konuya değinmiştik.

Küçük Projede Başka, Büyük Sistemde Başka Davranır mı?

Evet, resmen farklı hissedilir. Küçük projede birkaç global değişkenle idare etmek mümkün olabilir — kimse size bir şey demez, proje de çalışır. Ama proje büyüdükçe execution context bilgisinin değeri katlanarak artıyor. Mesela basit landing page’de scope zincirini kimse umursamayabilir. Fakat mikro servisleri olmayan, ama onlar kadar karmaşık tek repo yapılarda işler çabuk karışır.

Benim önerim şu: Her zaman modüler düşünün, global’e yaslanmayın, fonksiyon sınırlarını temiz tutun. Aksi halde kodunuz “çalışan ama açıklanamayan” kategoriye girer — bu durumun üstüne yeni özellik eklemek zorunda kalmak gerçekten kötü bir his, inanın.

Sahada İşe Yarayan Pratik İpuçları

Laf kalabalığını bırakıp gerçek hayata dönelim. Kod okurken genelde şu soruları sorun kendinize: Hangi scope’tayım? Bu isim nereden geliyor? ThisBinding bende mi yoksa yukarıda mı? Bu üç soru çoğu bug’ın ipucunu veriyor zaten.

İkinci ipucu: Küçük örnekler yazın. console.log ile ilerleyin, stack’in neresinde olduğunuzu takip edin. Teorik okumak yetmez — elle yazınca oturur.

Üçüncü ipucu: Hele bir de legacy projelerde isim çakışmasına dikkat edin. Bazen hata JavaScript’te değil, eski alışkanlıklarda gizlidir. Birileri yıllar önce global scope’a bir şeyler bırakmış, siz de üzerine yazıyorsunuzdur farkında olmadan.

Dördüncü ipucu biraz tatsız ama söylemek gerekiyor: Hoisting’i atlayıp “ben anladım” demeyin. Çoğu kişi orada takılıyor. Ben de dahil — o konuyu “anladım” dediğim gün aslında en yüzeysel noktasını öğrenmiştim sadece.

Sıkça Sorulan Sorular

> Execution context nedir?

> Execution context JavaScript’in kodu çalıştırmak için hazırladığı ortamdır. İçinde variable environment, lexical environment ve this bilgisi bulunur. Kısacası kodun sahne arkasıdır.

> Scope chain neden önemlidir?

> Çünkü JavaScript’in isimleri nereden bulacağını belirler.
Bir değişkeni mevcut scope’da bulamazsa dışa çıkar ve oradan bakar.
Bu yapı anlaşılmazsa debug süresi uzar.

> Global scope kullanmak kötü mü?

> Küçük örneklerde idare eder ama büyüyen projelerde risklidir.
Çünkü isim çakışmaları ve beklenmedik paylaşım problemleri çıkarabilir.
Temiz modüler yapı genelde daha sağlıklıdır.

> ThisBinding neden bazen değişiyor?

> Çünkü this', nasıl çağrıldığına göre farklı nesneleri gösterebilir
Normal fonksiyonlarda ve arrow function’larda davranış farklıdır
Bu yüzden bağlam okumak şarttır

Kaynaklar ve İleri Okuma

MDN — this Operatörü Dokümantasyonu

ECMAScript Resmî Spesifikasyonu

MDN — JavaScript Rehberi

Aşkın KILIÇ

20+ yıl deneyimli Azure Solutions Architect. Microsoft sertifikalı bulut mimari ve DevOps danışmanı. Azure, yapay zekâ ve bulut teknolojileri üzerine Türkçe teknik içerikler üretiyor.

AZ-305AZ-104AZ-500AZ-400DP-203AI-102

Bu içerik işinize yaradı mı?

Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.

Haftalık Bülten

Her pazar özenle seçilmiş teknoloji yazıları doğrudan e-postanıza gelsin.

← Onceki Yazi
Android’den Kaldırılan DDLC: Bir Oyunun İnce Çizgisi
Sonraki Yazi →
Anthropic’in Çifte Şoku: Mythos, Para ve Güvenlik Dengesi

Yorum Yaz

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Haftalık Bülten

Azure, DevOps ve Yapay Zeka dünyasındaki en güncel içerikleri her hafta doğrudan e-postanıza alın.

Spam yok. İstediğiniz zaman iptal edebilirsiniz.
📱
Uygulamayı Yükle Ana ekrana ekle, çevrimdışı oku
Kategoriler
Ara
Paylaş
İçindekiler
← Android’den Kaldırılan DDLC: B...
Anthropic’in Çifte Şoku: Mytho... →
📩

Gitmeden önce!

Her pazar özenle seçilmiş teknoloji yazıları ve AI haberleri doğrudan e-postanıza gelsin. Ücretsiz, spam yok.

🔒 Bilgileriniz güvende. İstediğiniz zaman ayrılabilirsiniz.

📬 Haftalık bülten: Teknoloji + AI haberleri