Javascript Motoru Nedir?
JavaScript motoru, tarayıcı veya Node.js gibi ortamlarda JavaScript kodunu analiz eden, çalıştıran ve gerektiğinde işlemcinin anlayabileceği makine koduna dönüştüren yazılımdır.
JavaScript yazarken aslında işlemciyle doğrudan iletişim kurmazsınız. Yazdığınız kodun çalıştırılabilmesi için önce JavaScript motoru tarafından işlenmesi gerekir.
En bilinen JavaScript motorları aşağıdaki tabloda gösterilmiştir.
| Ortam | JavaScript Motoru |
|---|---|
| Google Chrome | V8 |
| Microsoft Edge | V8 |
| Node.js | V8 |
| Mozilla Firefox | SpiderMonkey |
| Safari | JavaScriptCore |
JavaScript Motoruna Neden İhtiyaç Duyulur?
İşlemciler (CPU), JavaScript gibi yüksek seviyeli programlama dillerini doğrudan anlayamaz. Onlar yalnızca makine kodu adı verilen düşük seviyeli talimatları çalıştırabilir.
JavaScript motoru;
- Yazdığınız kodu analiz eder.
- Kodu çalıştırılabilir hâle getirir.
- Performansı artırmak için optimizasyonlar yapar.
- Bellek yönetimini otomatik olarak gerçekleştirir.
Kısacası JavaScript motoru olmasaydı yazdığımız JavaScript kodları çalıştırılamazdı.
JavaScript Motoru Nasıl Çalışır?
Bir JavaScript motoru kodu çalıştırırken temel olarak aşağıdaki adımları izler:
- Parsing (Ayrıştırma)
- AST Oluşturma
- Bytecode Üretimi
- Bytecode'un Çalıştırılması
- JIT Optimizasyonu
- Bellek Yönetimi
Şimdi bu adımların her birini inceleyelim.
1. Parsing (Ayrıştırma)
Kod motora ulaştığında yapılan ilk işlem Parsing'dir.
Bu aşamada motor kodu anlamaya çalışır.
Parsing işlemi iki bölümden oluşur.
Lexical Analysis (Sözcük Analizi)
İlk olarak kod küçük parçalara ayrılır.
Bu küçük parçalara Token adı verilir.
Örneğin aşağıdaki kod:
let age = 18;
Şu token'lara ayrılır:
| Kod | Token Türü |
|---|---|
| let | Keyword |
| age | Identifier |
| = | Operator |
| 18 | Number |
| ; | Punctuation |
Henüz kod çalıştırılmaz.
Motor yalnızca kodun parçalarını tanımaktadır.
Syntactic Analysis (Sözdizimi Analizi)
Oluşturulan token'lar bu kez JavaScript sözdizimine uygun mu diye kontrol edilir.
Örneğin aşağıdaki kod geçerlidir.
let age = 18;
Ancak aşağıdaki kod sözdizimi hatası içerir.
let = age 18;
Bu durumda JavaScript motoru bir SyntaxError oluşturur ve kod çalıştırılmaz.
AST (Abstract Syntax Tree)
Kodun sözdizimi doğruysa motor bir Abstract Syntax Tree (AST) oluşturur.
AST, kodun ağaç biçimindeki temsilidir.
Örneğin şu kod:
let age = 18;
AST içerisinde kabaca şu şekilde temsil edilir.
VariableDeclaration
│
├── Identifier (age)
│
└── Literal (18)
Gerçek AST bundan çok daha ayrıntılıdır ancak temel mantık aynıdır.
AST sayesinde JavaScript motoru kodun yapısını kolayca analiz edebilir.
AST oluşturulduktan sonra JavaScript motoru artık kodun hangi değişkenleri, fonksiyonları ve ifadeleri içerdiğini bilir.
2. Interpretation (Yorumlama)
AST oluşturulduktan sonra sıra kodun çalıştırılmasına gelir.
Motorun içindeki Interpreter (Yorumlayıcı), AST'yi alır ve Bytecode adı verilen ara bir koda dönüştürür.
JavaScript
│
▼
AST
│
▼
Interpreter
│
▼
Bytecode
Bytecode Nedir?
Bytecode;
- JavaScript kodu değildir.
- Makine kodu da değildir.
İkisinin arasında bulunan ara bir temsil biçimidir.
Interpreter ürettiği Bytecode'u hemen çalıştırmaya başlar.
Bu sayede JavaScript kodu tamamen derlenmeyi beklemeden çalıştırılabilir.
Bytecode kullanılması JavaScript'in hızlı açılmasının en önemli nedenlerinden biridir.
Neden Doğrudan Makine Koduna Çevrilmiyor?
Eğer JavaScript motoru her dosyayı önce tamamen makine koduna çevirmeyi bekleseydi;
- Sayfalar daha geç açılırdı.
- Küçük script'ler gereksiz yere derlenirdi.
- Başlangıç süresi uzardı.
Bu nedenle JavaScript motorları önce Bytecode üretir, ardından gerçekten gerekli olan kodları optimize eder.
3. JIT (Just-In-Time) Compilation
Modern JavaScript motorları yalnızca yorumlayıcı kullanmaz.
Aynı zamanda JIT Compiler (Just-In-Time Derleyici) adı verilen ikinci bir sistem de bulunur.
JIT'in amacı, sık çalışan kodları daha hızlı hâle getirmektir.
Bu süreç üç adımdan oluşur.
1. Profiling (İzleme)
Motor, program çalışırken hangi fonksiyonların sürekli çağrıldığını takip eder.
Örneğin:
function add(a, b) {
return a + b;
}
for (let i = 0; i < 100000; i++) {
add(i, i);
}
Burada add() fonksiyonu yüz bin kez çağrılmıştır.
Motor bunun çok sık kullanıldığını fark eder.
Bu tür kodlara Hot Code (Sıcak Kod) adı verilir.
2. Optimization (Optimizasyon)
JIT derleyicisi, Hot Code olarak işaretlenen bölümleri makine koduna dönüştürür.
JavaScript
│
▼
Bytecode
│
▼
Hot Code ?
│
Evet
│
▼
Machine Code
Makine kodu doğrudan işlemci tarafından çalıştırıldığı için Bytecode'a göre çok daha hızlıdır.
Bu nedenle JavaScript programları çalıştıkça hızlanabilir.
3. Deoptimization (Optimizasyonu Geri Alma)
JavaScript dinamik tipli bir programlama dilidir. Bu nedenle bir değişken zaman içinde farklı veri tipleriyle kullanılabilir.
Örneğin:
function add(a, b) {
return a + b;
}
add(5, 10);
add(20, 30);
add(1, 4);
Motor, add() fonksiyonunun sürekli sayılarla çağrıldığını gözlemleyebilir ve buna göre optimize edebilir.
Ancak daha sonra şu kod çalışırsa:
add("Merhaba ", "Dünya");
Artık motorun yaptığı varsayım geçerliliğini kaybeder.
Bu durumda JavaScript motoru yapılan optimizasyonu iptal eder (Deoptimization) ve fonksiyonu tekrar Bytecode seviyesinde çalıştırabilir.
4. Bellek Yönetimi (Memory Management)
JavaScript'te bellek yönetimi geliştiriciden büyük ölçüde gizlenmiştir.
Diğer birçok programlama dilinde belleğin manuel olarak yönetilmesi gerekirken JavaScript bunu otomatik olarak gerçekleştirir.
JavaScript motoru belleği temel olarak iki bölgede yönetir.
| Bellek Bölgesi | Görevi |
|---|---|
| Stack (Call Stack) | Fonksiyon çağrıları ve Execution Context'leri tutar. |
| Heap (Memory Heap) | Nesneler, diziler ve diğer referans tipindeki verileri saklar. |
Memory Heap
Heap, büyük ve dinamik verilerin saklandığı bellek alanıdır.
Örneğin:
const user = {
name: "Ahmet",
age: 18
};
Burada oluşturulan nesne Heap belleğinde tutulur.
Benzer şekilde diziler de Heap'te saklanır.
const numbers = [10, 20, 30, 40];
Fonksiyonlar da çoğu JavaScript motorunda Heap üzerinde tutulur.
Fonksiyonlar heap bölgesinde tutulurken, fonksiyon çağrıları stack bölgesinde tutulur.
Call Stack
Call Stack, JavaScript motorunun fonksiyon çağrılarını yönettiği bir yığın (stack) veri yapısıdır.
Bir fonksiyon çağrıldığında, o fonksiyon için bir Execution Context oluşturulur ve Call Stack'in en üstüne eklenir. Fonksiyonun çalışması tamamlandığında ise bu Execution Context Stack'ten kaldırılır.
Call Stack, LIFO (Last In, First Out) prensibiyle çalışır. Yani Stack'e en son eklenen Execution Context, ilk olarak kaldırılır.
Örneğin:
function hello() {
console.log("Merhaba");
}
function start() {
hello();
}
start();
Kodun çalışması sırasında Call Stack'in durumu adım adım aşağıdaki gibi değişir.
İlk durum:
┌─────────────┐
│ Global │
└─────────────┘
start() çağrıldığında:
┌─────────────┐
│ start() │
├─────────────┤
│ Global │
└─────────────┘
hello() çağrıldığında:
┌─────────────┐
│ hello() │
├─────────────┤
│ start() │
├─────────────┤
│ Global │
└─────────────┘
console.log() çalışırken:
┌─────────────────┐
│ console.log() │
├─────────────────┤
│ hello() │
├─────────────────┤
│ start() │
├─────────────────┤
│ Global │
└─────────────────┘
Fonksiyonlar tamamlandıkça Stack'ten sırayla çıkarılır.
Son durumda tekrar yalnızca Global Execution Context kalır.
Execution Context Nedir?
Her JavaScript kodu bir Execution Context (Çalıştırma Bağlamı) içerisinde çalışır.
Başlangıçta oluşturulan bağlama Global Execution Context denir.
Her fonksiyon çağrıldığında ise yeni bir Execution Context oluşturulur ve Call Stack'e eklenir.
function first() {
second();
}
function second() {
console.log("Merhaba");
}
first();
Bu örnekte sırasıyla şu Execution Context'ler oluşturulur.
- Global Execution Context
first()second()console.log()
Fonksiyon tamamlandığında ilgili Execution Context Call Stack'ten kaldırılır.
Execution Context ile Call Stack aynı şey değildir.
- Execution Context, kodun çalıştığı ortamdır.
- Call Stack ise bu ortamları takip eden veri yapısıdır.
Garbage Collector
Program çalışırken bazı nesneler artık kullanılmaz hâle gelir.
Örneğin:
let person = {
name: "Ali"
};
person = null;
Bu örnekte oluşturulan nesneye artık erişen hiçbir referans kalmamıştır.
JavaScript motoru uygun bir zamanda bu belleği otomatik olarak temizler.
Bu işlemi gerçekleştiren sisteme Garbage Collector (Çöp Toplayıcı) adı verilir.
Modern JavaScript motorları, temel olarak Mark-and-Sweep yaklaşımını kullanan gelişmiş çöp toplama algoritmaları kullanır.
Bu sayede kullanılmayan bellek zamanla geri kazanılır.
Garbage Collector'ın ne zaman çalışacağına geliştirici karar veremez. Bu tamamen JavaScript motorunun kontrolündedir.
JavaScript Motorunun Çalışma Süreci
Bir JavaScript motorunun kodu çalıştırma süreci özetle aşağıdaki gibidir.
JavaScript Kodu
│
▼
Parsing
│
▼
Token'lar
│
▼
AST
│
▼
Interpreter
│
▼
Bytecode
│
▼
Program Çalışır
│
▼
Profiling
│
▼
Hot Code
│
▼
JIT Compiler
│
▼
Machine Code
Kod çalışırken JavaScript motoru aynı zamanda Heap ve Call Stack'i yönetmeye devam eder.
Kullanılmayan bellek ise Garbage Collector tarafından temizlenir.
Özet
Bu derste JavaScript motorunun çalışma mantığını öğrendik.
Özetle süreç şu şekilde ilerler:
- JavaScript kodu Parsing işleminden geçer.
- Koddan AST oluşturulur.
- AST, Interpreter tarafından Bytecode'a dönüştürülür.
- Bytecode çalıştırılır.
- Sık kullanılan kodlar JIT derleyicisi tarafından makine koduna optimize edilir.
- Gerekirse optimizasyon geri alınabilir (Deoptimization).
- Heap ve Call Stack sürekli yönetilir.
- Kullanılmayan bellek Garbage Collector tarafından temizlenir.
Kendinizi Test Edin
1. JavaScript motorunun yaptığı ilk işlem hangisidir?
- A) Garbage Collection
- ✅ B) Parsing
- C) Optimization
- D) Compilation
2. AST'nin görevi nedir?
- A) Belleği temizlemek
- ✅ B) Kodun yapısını ağaç biçiminde temsil etmek
- C) CSS oluşturmak
- D) HTTP isteği göndermek
3. Bytecode nedir?
- A) Makine kodu
- B) JavaScript kodu
- ✅ C) JavaScript ile makine kodu arasında bulunan ara temsil biçimi
- D) Assembly dili
4. Hot Code neyi ifade eder?
- A) Hatalı çalışan kod
- B) Asenkron çalışan kod
- ✅ C) Çok sık çalıştırılan kod
- D) Büyük boyutlu kod
5. Garbage Collector'ın görevi nedir?
- A) Kodu derlemek
- B) Bytecode oluşturmak
- ✅ C) Kullanılmayan belleği temizlemek
- D) AST oluşturmak
Sonraki Konu
Artık JavaScript motorunun kodu nasıl çalıştırdığını biliyorsunuz.
Bir sonraki derste ise Call Stack, Web API'leri, Callback Queue, Microtask Queue ve Event Loop'un birlikte nasıl çalıştığını öğreneceğiz.