JavaScript

JavaScript’te “Lexical Environment”, “Scope” ve “Scope Chain” Kavramları

Bu yazımızda, JavaScript’te bulunan “Lexical Environment”, “Scope” ve “Scope Chain” kavramları üzerine yoğunlaşacağız. Bu kavramlar, değişkenlerin, fonksiyonların ve diğer tanımlamaların erişilebilirliği ve yaşam döngüsü üzerinde derin bir anlayış kazanmamıza yardımcı olacağı için çok ama çok önemlidir.

“Lexical Environment”, “Scope” ve “Scope Chain” Kavramları

  • Lexical Environment” kavramı, bir fonksiyonun veya kod bloğunun değişkenlerini, fonksiyonlarını vb. sakladığı yerdir ve küme parantezlerinin ({ }) kullanıldığı her yerde otomatik olarak oluşturulur.
  • Scope” (kapsam) kavramı, bir değişkenin veya fonksiyonun hangi bölgeden erişilebilir olduğunu tanımlar.
  • Scope Chain” (kapsam zinciri) kavramı, bir değişkeni veya fonksiyonu mevcut kapsamda bulamadığımızda nereye başvuracağımızı belirleyen yapıdır.

Tanımlar kafa karıştırıcı olabileceğinden ilgili kavramlarımızı daha iyi anlatabilmek adına uzay ve gezegenler metaforunu kullanacağım:

Öncelikle, “lexical environment” yapısını bir tür çevreleyici veya atmosfer olarak düşünmenizi istiyorum. Bu çevreleyici yapının sınırlarını, bir tür uzayın sınırları olarak kabul edelim. JavaScript’in tüm evrenini temsil eden en dıştaki yapı, en geniş “lexical environment” olarak düşünülebilir. Bu, tüm kodumuzun bulunduğu ana evreni temsil eder.

Bu büyük ve sınırsız evrenin içerisinde, farklı özelliklerde ve büyüklüklerde birçok gezegen bulunmaktadır. Her bir gezegenin kendi atmosferi vardır ve bu atmosferlerin de kendi içerisinde birer “lexical environment” olduğunu hayal edebilirsiniz.

Bu geniş JavaScript evrenindeki gezegenler, aslında kod bloğundan oluşmuş yapılardır. Başka bir deyişle, JavaScript’te küme parantezlerinin ({ }) kullanıldığı her yerde, otomatik olarak bir “lexical environment” oluşturulur. Bu, her bir gezegenin kendi atmosferini oluşturması gibidir. Bu atmosferler, gezegenlerin – yani kod bloklarının – içerisindeki tüm değişkenleri, fonksiyonları ve diğer yapıları korur ve saklar.

Her bir gezegenin atmosferi içinde kalan yapılar da fonksiyon ya da kod bloğunu temsil ettiğini varsayalım. Yani gezegendeki dağlar, vadiler veya göller, o fonksiyon ya da kod bloğu içerisinde tanımlanmış değişkenleri, fonksiyonları ve diğer yapıları temsil etsin. İşte bu doğal yapıları “scope” yani kapsam olarak düşünebiliriz. Bir dağın zirvesindeki bir bitki veya taş, sadece o dağın zirvesinde bulunabilir. Eğer bu bitki veya taşı arıyorsanız, doğrudan o dağın zirvesine gitmeniz gerekir. Aynı şekilde, bir fonksiyon içerisinde tanımlı bir değişkeni sadece o fonksiyon içerisinden erişebiliriz.

Eğer bir dağın zirvesinde aradığınızı bulamazsanız, belki yanındaki diğer dağa veya vadiye bakmanız gerekebilir. Bu, bir fonksiyonun kapsamında aradığınızı bulamadığınızda, dış kapsamlara doğru bakma sürecini temsil eder. İşte bu, en yakından en uzağa, yani bir alt kümeden üst kümeye devam eden yolculuk, “scope chain” kavramının ne olduğunu anlatır. Aradığınız şeyi bulana kadar bir alt kümeden bir üst kümeye yolculuğunuz devam eder. Eğer tüm evrende, yani tüm “lexical environment”lerde aradığınızı bulamazsanız, bu, aradığınız şeyin bu evrende olmadığı, yani tanımlanmadığı anlamına gelir.

Görüleceği üzere “scope chain” (kapsam zinciri) konsepti, en içteki kapsamdan (örneğin bir fonksiyon içindeki kapsam) başlar ve gerektiğinde bir üst kapsama, daha sonra onun da üstüne doğru devam eder. Bu süreç, en dıştaki, genellikle global kapsama (en üst kapsam) kadar devam eder.

“Lexical Environment” Nedir?

“Lexical environment” (Sözcüksel Çevre), JavaScript’te kodun çalışma zamanında değişkenlerin, fonksiyonların ve diğer yapıların nasıl ve nerede saklandığını tanımlayan bir konsepttir. Temelde, her fonksiyon çalıştırıldığında veya bir kod bloğu (örneğin, bir if bloğu veya bir döngü) başlatıldığında, yeni bir “Lexical Environment” oluşturulur. Bu leksiksel çevre, o kapsamdaki tüm yerel değişkenleri, fonksiyonları ve mevcut scope chain’i saklar.

Bu yapı iki ana bileşenden oluşur:

  1. Environment Record: Mevcut kapsamdaki tüm değişkenleri, fonksiyonları ve diğer bağlam bilgilerini içerir.
  2. Outer Reference: Dıştaki (üstteki) sözcüksel çevreyi, yani bir üst kapsamı (eğer varsa) gösterir. Bu, “scope chain” konseptinin temelini oluşturur.

Kodumuzda, en başta globalVariable adında global bir değişken tanımladık. Bu değişkenin değerini “Global” olarak atadık ve kodun en dışında yer aldığı için tüm fonksiyonlardan erişilebilir bir konumda bulundu.

Daha sonrasında, outerFunction adında bir fonksiyon tanımladık. Bu fonksiyonun içerisinde, outerVariable adında başka bir değişken daha tanımladık ve bu değişkenin değerini “Outer” olarak atadık. outerVariable, outerFunction fonksiyonunun sözcüksel çevresi içerisinde tanımlandığı için sadece bu fonksiyon ve içerisindeki, yani alt kümesi olan diğer fonksiyonlar tarafından erişilebilir oldu.

Bu fonksiyonun içinde, innerFunction adında bir başka fonksiyon daha tanımladık. Bu iç fonksiyon, innerVariable adında kendi değişkenini tanımladık ve bu değişkenin değerini “Inner” olarak atadık. innerFunction, kendi sözcüksel çevresindeki değişkenlere, outerFunction‘ın sözcüksel çevresindeki değişkenlere ve aynı zamanda global değişkenlere erişebildi. Bu nedenle, bu fonksiyon içerisinde üç farklı değişkenin değerini ekrana yazdırdı.

Son olarak, kodun en altında outerFunction fonksiyonunu çağırdık. Bu çağrıyla birlikte, outerFunction içerisinde tanımlı olan innerFunction fonksiyonu da otomatik olarak çalıştırıldı. Bu sayede, üç değişkenin değeri sırasıyla ekrana basıldı.

“Scope” Nedir?

“Scope” ya da dilimize çevirirsek “kapsam”, JavaScript’te değişkenlerin, fonksiyonların ve nesnelerin hangi bölümde erişilebilir olduğunu tanımlayan bir konsepttir. Değişkenlerin nerede tanımlandığına göre erişilebilirliği ve yaşam süresi değişir. JavaScript’te iki ana tür kapsam vardır:

  1. Global Scope (Global Kapsam): Kodun en üst seviyesinde tanımlanan değişkenler ve fonksiyonlar global kapsamda bulunurlar ve kodun her yerinden erişilebilirler.
  2. Local Scope (Yerel Kapsam): Fonksiyonlar içinde tanımlanan değişkenler ve fonksiyonlar yerel kapsamdadır ve sadece o fonksiyonun içinden erişilebilirler.

Kodumuzda, en başta tanımlanan değişkenler ve fonksiyonlar global kapsamda yer alır. Bu, tüm fonksiyonlar ve iç içe fonksiyonlar tarafından erişilebilir olduğu anlamına gelir. Ancak, bir fonksiyon içerisinde tanımlanan değişkenler ve fonksiyonlar sadece o fonksiyonun kapsamında erişilebilir. Eğer bir fonksiyon içerisinde başka bir fonksiyon tanımlanırsa, içteki fonksiyon dıştaki fonksiyonun kapsamında tanımlı değişkenlere erişebilir. Ancak tersi doğru değildir; dıştaki fonksiyon, içteki fonksiyonun kapsamında tanımlı değişkenlere erişemez. Örneğin:

Bu kodda, globalVariable adında global bir değişken tanımladık. Sonrasında, firstFunction adında bir fonksiyon oluşturduk. Bu fonksiyonun içerisinde, firstVariable adında bir değişken daha tanımladık. Aynı zamanda, bu fonksiyonun içerisinde secondFunction adında başka bir fonksiyon daha tanımladık. secondFunction fonksiyonu, kendi kapsamında tanımlı secondVariable değişkenine, bir üst kapsamda olan firstFunction fonksiyonunun kapsamında tanımlı firstVariable değişkenine ve global kapsamda tanımlı globalVariable değişkenine erişebilir.

Ancak firstFunction fonksiyonu, sadece kendi kapsamında ve global kapsamda tanımlı değişkenlere erişebilir; secondFunction fonksiyonunun kapsamında tanımlı değişkenlere erişemez. Bu nedenle, secondVariable değişkenine firstFunction fonksiyonunun içerisinden erişmeye çalıştığımızda bir hata alırız.

“Scope” hakkında daha detaylı bir bilgi için JavaScript’teki scope ve closure yazısını inceleyebilirisiniz.

“Scope Chain” Nedir?

JavaScript’teki “lexical environment” ve “scope” kavramlarını anladıysanız “scope chain” kavramını anlamak oldukça kolay olacaktır.

Değişkenlerin erişilebilirliğini belirlemek için kullanılan “scope chain”, bir değişkenin erişilebilirliğinin, o değişkenin tanımlandığı scope’tan (kapsamdan) başlayarak yukarı doğru hareket ederek kontrol edildiğini anlatır.

Yani “scope chain”, JavaScript’te bir değişkenin ya da fonksiyonun nerede aranacağını belirleyen bir mekanizmadır. Basitçe ifade edersek, bir değişkene ya da fonksiyona erişmeye çalıştığınızda, JavaScript motoru öncelikle bulunduğunuz kapsamda bu değişkeni ya da fonksiyonu arar. Eğer bulamazsa, bir üst kapsama geçer ve aramaya devam eder. Bu süreç, aranan değişken ya da fonksiyon bulunana kadar ya da en dıştaki global kapsama kadar devam eder. İşte bu kapsamlar arasındaki bağlantıya “Scope chain” denir.

Bu kodda, secondFunc fonksiyonu içerisinden secondVar adlı değişkene doğrudan erişebiliriz çünkü bu değişken aynı kapsamda tanımlıdır. Eğer secondFunc içerisinde tanımlı olmayan bir değişkeni ararsak, JavaScript motoru bu değişkeni bulmak için bir üst kapsama, yani firstFunc‘a gider. Bu yüzden firstVar adlı değişkene de erişebiliriz. Eğer bu kapsamda da aradığımızı bulamazsak, bu sefer global kapsama gidilir ve globalVar adlı değişkene erişebiliriz.

Bu örnek, bir değişken ya da fonksiyon arama sürecinin nasıl işlediğini ve kapsamlar arasındaki bu bağlantının, yani “scope chain”in nasıl oluştuğunu göstermektedir.

Birbaşka örnek olarak aşağıdaki örnek, scope chain’in her bir fonksiyonu nasıl birbirine bağladığını gösterecektir:

Yukarıdaki örnek, iç içe yerleştirilmiş üç fonksiyonu kullanarak, değişkenlerin scope chain içinde nasıl hareket ettiğini gösterir.

İlk olarak sayMyName adlı bir fonksiyon tanımlanır. Bu fonksiyon, içinde a adlı bir değişken tanımlar ve bir console.log ifadesi kullanarak, b ve c değişkenlerine erişmeye çalışır. Ancak, b ve c henüz tanımlanmadığından hata verir.

Daha sonra sayMyName fonksiyonu içinde, findName adlı bir başka fonksiyon tanımlanır. Bu fonksiyon, içinde b adlı bir değişken tanımlar ve a değişkenine erişir. Ancak, kendi kapsamı içinde c henüz tanımlanmadığından, console.log ifadesi hata verir.

Son olarak, findName fonksiyonu içinde, printName adlı bir başka fonksiyon tanımlanır. Bu fonksiyon, içinde c adlı bir değişken tanımlar. Scope chain sayesinde hem a hem de b değişkenlerine erişir. Kabaca diyebiliriz ki, çocuklar atanın özelliklerini miras alabilir ama ata, çocuklarının özelliklerini miras alamaz.

Bu örnek, iç içe yerleştirilmiş fonksiyonların scope chain içinde nasıl hareket ettiğini ve değişkenlerin hangi kapsamlardan erişilebilir olduğunu gösterir. Değişkenlerin, tanımlandıkları kapsamdan başlayarak yukarı doğru hareket eden scope chain üzerinde arandığına dikkat edin. Ayrıca, var anahtar sözcüğü ile tanımlanan değişkenlerin, deklare edildikleri kapsamın başında hoisted olduğunu unutmayın.

İlgili Makaleler

Bir yanıt yazın

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

Başa dön tuşu

😔 Reklam Engelleyicisi 😔

Sitenin varlığını sürdürebilmesi adına reklam engelleyicinizi (Adblocker) kapatmanızı rica ediyoruz 😔