Mert Özen Her Satırda Daha İleri
Frontend JavaScript Temel — #02

JavaScript ile İlk Script: script Etiketi, defer, async ve DevTools

Mert Özen 3 Kas 2026 9 dk 5 görüntülenme
JavaScript ile İlk Script: script Etiketi, defer, async ve DevTools

JavaScript'i HTML'e inline ve harici dosya olarak bağlıyor, defer ile async farkını görüyor, DevTools'ta console method'larını, hata mesajı okumayı ve ilk breakpoint'imizi deniyoruz.

Sürüm notu: Bu yazıdaki kodlar ES2026 ve güncel tarayıcılar (Chrome, Edge, Firefox, Safari) içindir.

Önce Tahmin Et

Bu sefer konsol yerine küçük bir HTML dosyasıyla başlıyoruz. Aşağıdaki kodu bilgisayarında predict.html adıyla kaydet ama açmadan önce bir dakika dur.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Predict</title>
  <script>
    console.log(document.body);
  </script>
</head>
<body>
  <h1>Hello</h1>
</body>
</html>

Dosyayı tarayıcıda açıp konsola baktığında ne göreceksin? body etiketinin kendisini mi, bir hatayı mı, yoksa bambaşka bir şeyi mi? Okumaya devam etmeden önce bir tahmin yap. Cevabı, tarayıcının bir HTML dosyasını nasıl okuduğunu konuştuktan sonra vereceğiz.

script Etiketi: JavaScript'in Sayfaya Giriş Kapısı

Daha önce gördüğümüz konsol hızlı denemeler için birebir, ama yazdığın her şey sayfayı yenilediğin anda uçup gidiyor. Kodun kalıcı olması için onu sayfanın bir parçası yapman gerekiyor. Bunun yolu <script> etiketi.

En basit hâliyle kodu doğrudan etiketin içine yazarsın. Buna inline script denir:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>First Script</title>
</head>
<body>
  <h1>Hello</h1>
  <script>
    console.log("Page loaded, the script ran.");
  </script>
</body>
</html>

Bunu first.html olarak kaydet, dosyaya çift tıklayıp tarayıcıda aç, sonra konsolu aç. Mesajı göremiyorsan konsol açıkken sayfayı bir kez yenile. Ne sunucu var ne kurulum; tarayıcı dosyayı diskten okuyup çalıştırdı.

Harici Dosya: Kodu Kendi Evine Taşımak

Inline script birkaç satır için iş görür, ama kod büyüdükçe HTML'in içinde kaybolmaya başlar. Asıl yaygın yöntem, JavaScript'i ayrı bir .js dosyasına koyup src attribute'u ile bağlamaktır. Aynı klasörde iki dosya oluştur. Önce index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>External Script</title>
</head>
<body>
  <h1>Hello</h1>
  <script src="app.js"></script>
</body>
</html>

Sonra yanına app.js:

console.log("Greetings from app.js!");

Burada dikkat etmen gereken birkaç şey var. src verdiğin bir <script> etiketinin içine kod yazarsan o kod yok sayılır; ya dosya ya içerik, ikisi birden olmaz. Etiketi her zaman </script> ile kapatman gerekir. <script src="app.js" /> gibi kendini kapatan bir yazım HTML'de işe yaramaz: tarayıcı etiketi hâlâ açık sayar ve arkasından gelen HTML'i de script'in içeriği zanneder. JS dosyasının içinde ise <script> etiketi olmaz; orası saf JavaScript.

Ayrı dosyanın faydası sadece düzen değil: tarayıcı onu önbelleğe alabilir, sen de aynı dosyayı birden fazla sayfada kullanabilirsin.

Script'i Nereye Koymalı?

Şimdi baştaki soruya dönelim. Cevap: konsolda null görürsün.

Nedenini anlamak için tarayıcının HTML'i nasıl okuduğunu bilmek gerekiyor. Tarayıcı dosyayı yukarıdan aşağıya okur ve okudukça sayfanın yapısını kurar. Bir <script> etiketine geldiğinde durur, script'i çalıştırır, iş bitince okumaya kaldığı yerden devam eder. Script harici bir dosyaysa önce onun inmesini de bekler.

Tahmin dosyamızda script <head> içinde. Tarayıcı o satıra geldiğinde <body> etiketini henüz okumamış; ortada bir body yok. document.body bu yüzden null döner, yani "burada henüz bir şey yok" der. Aynı script'i </body> kapanışının hemen önüne taşırsan konsolda body element'ini görürsün, çünkü o noktaya gelindiğinde sayfanın gövdesi zaten kurulmuştur.

Bunu plana bakarak ev yapan bir usta gibi düşünebilirsin. Usta duvarları örerken her script satırında elindeki işi bırakıp talimatı uyguluyor. Talimat "mutfak dolabını boya" diyorsa ve mutfak daha yapılmadıysa boyanacak bir şey yok. Talimat geç gelirse de usta boş bekliyor, kullanıcı da o sırada beyaz bir ekrana bakıyor.

Uzun yıllar boyunca çözüm, bütün script'leri </body>'den hemen önceye koymaktı. Hâlâ çalışan bir yöntem. Ama bugün elimizde daha iyi bir araç var.

defer ve async: İndir Ama Beni Bekletme

Harici script'lere iki attribute ekleyebilirsin: defer ve async. İkisi de aynı cümleyle başlar: "Bu dosyayı arka planda indir, HTML'i okumayı durdurma." Fark, indirme bittikten sonra ne olduğunda.

<head>
  <meta charset="UTF-8">
  <title>defer and async</title>
  <script src="app.js" defer></script>
  <script src="counter.js" async></script>
</head>

defer şunu der: "İndir, ama sayfanın tamamı okunana kadar çalıştırma." Birden fazla defer'li script varsa, sayfa okunduktan sonra HTML'de yazıldıkları sırayla çalışırlar. Tahmin dosyandaki satırı harici bir dosyaya taşıyıp defer eklersen, etiket <head> içinde dursa bile null yerine body'yi görürsün.

async ise şunu der: "İndir, iner inmez çalıştır." İndirme bittiği anda tarayıcı HTML okumayı kısa bir süre durdurur ve script'i çalıştırır. Hangi dosyanın önce ineceği belli olmadığı için birden fazla async script'in sırası garanti değildir ve çalıştıkları anda sayfa henüz tam okunmamış olabilir.

Kargo örneğiyle düşünelim. Normal script, kitap okurken kapıda kargo beklemek gibidir; paket gelene kadar tek sayfa çeviremezsin. defer, siparişi verip okumaya devam etmek ve kitap bitince paketleri sipariş sırasına göre açmaktır. async da siparişi verip okumaya devam etmek, ama hangi paket gelirse okumayı bırakıp onu hemen açmaktır.

Pratik kural şu: sayfayla uğraşan ve birbirine bağlı script'lerin için defer kullan ve onları <head> içine koy. Sayfaya da diğer script'lere de hiç dokunmayan, tek başına çalışan bir ziyaret sayacı gibi şeyler için async uygundur. Emin değilsen defer seç.

İki ince not. Bu attribute'lar src ile bağlanan harici script'ler içindir; inline bir script'e defer yazarsan tarayıcı bunu görmezden gelir. Ayrıca type="module" ile yüklenen script'ler kendiliğinden defer gibi davranır; module'leri ilerleyen yazılarda detaylandıracağız.

Console Sadece log'dan İbaret Değil

Daha önce gördüğümüz console.log'un birkaç kardeşi var ve her biri mesaja farklı bir anlam yükler. Bunları tarayıcının konsoluna yapıştırıp dene:

console.log("Plain info", 42, true);
console.warn("Heads up: stock is running low");
console.error("Payment failed");

console.log virgülle ayırdığın her değeri yan yana yazar. console.warn sarı bir uyarı, console.error kırmızı bir hata olarak görünür. Konsolun üstündeki seviye filtresiyle yalnızca uyarıları ya da hataları gösterebilirsin; mesajlarını doğru seviyede yazarsan kalabalık bir konsolda aradığını çabuk bulursun.

Bir de console.table var. Birden fazla kaydı tablo hâlinde gösterir:

const products = [
  { name: "Pen", price: 15 },
  { name: "Notebook", price: 40 },
  { name: "Eraser", price: 8 }
];
console.table(products);

Burada köşeli parantezle bir array, süslü parantezle de object'ler kurduk; ikisini de ilerleyen yazılarda detaylandıracağız. const'u bir sonraki yazıda göreceğiz, şimdilik "bu değere bir isim veriyorum" diye oku. Konsolda her ürünün bir satır, name ve price'ın birer sütun olduğu düzgün bir tablo çıkar.

Hata Mesajını Okumak

Hata mesajı ilk bakışta korkutucu görünür ama aslında sana neyin nerede bozulduğunu söyleyen bir nottur. Az önceki index.html'i kullanalım. app.js'in içeriğini şununla değiştir ve sayfayı yenile:

console.log("Started");
consol.log("This line will throw an error");
console.log("We never get here");

Konsolda önce "Started" yazar, ardından kırmızı bir satır gelir: Uncaught ReferenceError: consol is not defined. Bu satırı parça parça oku. Uncaught, hatanın hiçbir yerde yakalanmadığını söyler; hata yakalamayı ilerleyen yazılarda detaylandıracağız. ReferenceError hatanın türüdür: motor, adını verdiğin bir şeyi bulamadı. consol is not defined da açıklamanın kendisi. Satırın sağında duran app.js:2 bağlantısı hatanın hangi dosyanın hangi satırında olduğunu gösterir; tıklarsan doğrudan o satıra gidersin.

Bir şeye daha dikkat et: üçüncü satır hiç çalışmadı. Yakalanmayan bir hata o script'in çalışmasını tam o noktada keser. Sayfadaki diğer <script> etiketleri ise bundan etkilenmez, kendi sıraları gelince çalışırlar.

Başlangıçta en sık karşılaşacağın üç tür var. ReferenceError: tanımlı olmayan bir isim kullandın, çoğu zaman bir yazım hatası. TypeError: bir değerle desteklemediği bir şey yapmaya çalıştın; örneğin "hello".toUppercase() yazarsan, doğrusu toUpperCase olduğu için "bu bir fonksiyon değil" anlamına gelen is not a function hatasını alırsın. SyntaxError: kod dilin kurallarına uymuyor, kapanmamış bir parantez ya da tırnak gibi.

Bu sonuncusu ötekilerden farklı davranır. Motor dosyayı çalıştırmadan önce baştan sona okur; kurala uymayan bir şey görürse hiçbir satırı çalıştırmaz. Yani dosyanın son satırında bir parantez eksikse, ilk satırdaki console.log bile çalışmaz.

Mesajın metni tarayıcıya göre biraz değişebilir ama tür ve satır numarası her yerde var. Bir hata gördüğünde ilk iş: türünü oku, mesajı oku, satıra tıkla.

İlk Breakpoint: Kodu Durdurup İçine Bakmak

console.log ile değer yazdırmak hata ayıklamanın en hızlı yolu, ama bazen kodu tam bir satırda dondurup o anki durumu incelemek istersin. Breakpoint bunu yapar. app.js'i şöyle değiştir:

const price = 120;
const discountRate = 0.25;
const discount = price * discountRate;
const total = price - discount;
console.log("Discounted price:", total);

Geliştirici araçlarında Chrome ve Edge'de Sources, Firefox'ta Debugger sekmesine geç. Dosya listesinden app.js'i aç ve 4. satırın numarasına tıkla; satır işaretlenir. Şimdi sayfayı yenile.

Kod 4. satırda durur ve sayfa bekleme moduna geçer. Bu satır henüz çalışmamıştır, çünkü breakpoint satırı çalıştırmadan hemen önce durdurur. Fareyi discount kelimesinin üstüne getirirsen 30 değerini görürsün. Yandaki Scope panelinde de o anda erişilebilen değişkenler listelenir. Chrome, Edge ve Firefox'ta F10 ile bir satır ilerler, F8 ile kodu kaldığı yerden devam ettirirsin.

Aynı şeyi kodun içinden de yapabilirsin. Durmak istediğin yere debugger; yazman yeterli:

const price = 120;
const discountRate = 0.25;
const discount = price * discountRate;
debugger;
const total = price - discount;
console.log("Discounted price:", total);

Geliştirici araçları açıkken tarayıcı bu satırda durur, kapalıyken satırı fark etmeden geçer. İşin bitince debugger satırlarını silmeyi unutma; yoksa bir gün paneli açtığında sayfan beklemediğin bir yerde donar.

"use strict" Üzerine Kısa Bir Not

Eski kodlarda dosyanın en başında tek başına duran tuhaf bir string görebilirsin:

"use strict";
counter = 1;
console.log(counter);

Bu satır, 2009'da ES5 ile gelen strict mode'u açar. Strict mode, JavaScript'in eskiden sessizce geçiştirdiği bazı hataları gerçek hataya çevirir. Örnekteki counter hiç tanımlanmadan kullanılıyor. Normal modda tarayıcı buna ses çıkarmaz ve arka planda global bir değişken oluşturur; strict mode'da ise ReferenceError: counter is not defined alırsın. İkincisi çok daha iyidir, çünkü yazım hatan ilk anda ortaya çıkar.

Direktifin işe yaraması için dosyanın ya da fonksiyonun ilk ifadesi olması gerekir. İyi haber şu: type="module" ile yüklenen module'ler kendiliğinden strict çalışır, bu yüzden modern kodda bu satırı giderek daha az göreceksin. Bu serideki örnekleri strict mode'da da sorunsuz çalışacak şekilde yazacağız.

Küçük Bir Deneme

★☆☆ Bir index.html ve bir app.js oluştur, script'i defer ile <head> içinden bağla. app.js'te console.log, console.warn ve console.error ile üç mesaj yazdır, sonra konsolun filtresiyle yalnızca hataları göster.

★★☆ Çıktıyı tahmin et: <head> içinde <script src="one.js" defer></script> ve <script src="two.js" defer></script> var, </body>'den hemen önce de console.log("inline") içeren bir inline script duruyor. one.js "one", two.js "two" yazdırıyor. Konsolda hangi sırayı görürsün? Sonra iki defer'i async ile değiştir ve sayfayı birkaç kez yenile; sıra hakkında neyi garanti edebilirsin, neyi edemezsin?

★★★ app.js'in ilk satırına console.log("first line"), son satırına da kapanış parantezi eksik bir console.log("last line" yaz. "first line" yazacak mı? Tahmin et, dene, konsoldaki hata türünü ve satır numarasını oku. Hatayı düzelttikten sonra ikinci satıra bir breakpoint koy ve F10 ile dosyanın sonuna kadar adım adım ilerle.

Bu yazıda const'u "bir değere isim vermek" diye geçiştirdik. Bir sonraki yazıda değişkenlerin kendisine odaklanacağız: let ve const arasındaki fark, block scope, isimlendirme kuralları ve var'ın neden artık emekliye ayrıldığı.