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

JavaScript String'leri: Template Literal, String Method'ları ve Değişmezlik

Mert Özen 3 Kas 2026 8 dk 6 görüntülenme
JavaScript String'leri: Template Literal, String Method'ları ve Değişmezlik

Tırnak türlerinden template literal'e, at() ile sondan erişimden slice, includes, replaceAll, split ve padStart'a kadar string'leri öğreniyor; değişmezliği, Türkçe İ ve emoji tuzaklarını görüyoruz.

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

Önce Tahmin Et

Daha önce döngüler yazısında bir string'in harflerini for...of ile tek tek gezmiştik. Bu yazıda string'in kendisine odaklanıyoruz. Başlamadan önce şu dört satıra bak:

let language = "javascript";
language[0] = "J";
language.toUpperCase();
console.log(language);

Konsola ne yazdırır? "Javascript" mı, "JAVASCRIPT" mı, yoksa bambaşka bir şey mi? Okumaya devam etmeden önce bir tahmin yap. Cevabı, string'lerin neden değişmediğini konuştuğumuz bölümde vereceğiz.

Üç Tür Tırnak

JavaScript'te bir string'i üç şekilde yazabilirsin: tek tırnak, çift tırnak ve backtick (ters tırnak). Bunu tarayıcının konsoluna yapıştırıp dene:

const single = 'Hello';
const double = "Hello";
const backtick = `Hello`;
console.log(single === double, double === backtick); // true true

Üçü de aynı değeri üretir. Tek ve çift tırnak arasında hiçbir fark yok; ekipler genelde birini seçip bütün projede onu kullanır. Asıl fark, işin içine değişken girince ortaya çıkıyor. Eskiden metni parça parça + ile birleştirirdik:

const userName = "Ada";
const age = 28;
console.log("Hello " + userName + ", you are " + age + " years old.");

Çalışır ama boşlukları ve tırnakları saymaktan gözün yorulur. ES2015 ile gelen template literal bu işi çok daha okunur hâle getirdi. Backtick içinde ${...} yazdığın her yere bir ifade koyabilirsin; motor o ifadeyi hesaplar ve sonucu metnin içine yerleştirir:

const userName = "Ada";
const age = 28;
console.log(`Hello ${userName}, you are ${age} years old.`);
console.log(`Next year you'll be ${age + 1}.`);
console.log(`Status: ${age >= 18 ? "adult" : "child"}`);

Süslü parantezin içi yalnızca değişken adı için değil. Toplama, daha önce gördüğümüz ternary operatörü, hatta bir fonksiyon çağrısı bile olabilir. Template literal'in ikinci marifeti çok satırlı metin. Kodda Enter'a bastığın yerde metin de gerçekten alt satıra geçer:

const poem = `First line
Second line
Third line`;
console.log(poem);

Kaçış Karakterleri

Peki çift tırnaklı bir string'in içine çift tırnak koymak istersen? Ya da normal bir string'de alt satıra geçmek? Burada ters bölü (\) devreye giriyor. Ters bölü motora "sıradaki karakteri düz okuma, özel anlamına bak" der:

console.log("She told me \"tomorrow\"."); // She told me "tomorrow".
console.log('It\'s fine');               // It's fine
console.log("Line 1\nLine 2");           // iki ayrı satır
console.log("Name:\tAda");               // araya bir tab girer
console.log("C:\\Users\\ada");           // C:\Users\ada

En sık kullanacakların bunlar: \n yeni satır, \t tab, \" ve \' tırnak, \\ ise ters bölünün kendisi. Pratikte tırnak kaçırmaya pek gerek kalmaz. İçinde çift tırnak geçen bir metni tek tırnakla ya da backtick'le yazarsın, olur biter.

Uzunluk ve Karakterlere Erişim

Bir string'i yan yana dizilmiş, numaralı kutucuklar gibi düşün. Her kutuda bir karakter var ve numaralar sıfırdan başlıyor. Kaç kutu olduğunu length söyler, belirli bir kutuya köşeli parantezle ulaşırsın:

const word = "lemon";
console.log(word.length);           // 5
console.log(word[0]);               // "l"
console.log(word[4]);               // "n"
console.log(word[10]);              // undefined
console.log(word[word.length - 1]); // "n"

length bir method değil, property; sonuna parantez koymazsın. Olmayan bir index'e baktığında hata almazsın, undefined gelir. Son karakter için yazdığımız word[word.length - 1] kalıbı ise hem uzun hem de okuması zahmetli. ES2022 ile gelen at() method'u tam burada işe yarıyor. Negatif bir sayı verirsen sondan saymaya başlar:

const word = "lemon";
console.log(word.at(0));  // "l"
console.log(word.at(-1)); // "n"
console.log(word.at(-2)); // "o"
console.log(word[-1]);    // undefined

Son satır önemli. Köşeli parantez negatif index'ten anlamaz; "-1" adında bir kutu arar, bulamaz ve undefined döner. Sondan saymak istiyorsan at() kullan.

String'in İçinde Arama

Bir metnin içinde başka bir parça var mı diye bakmak, gündelik işlerin en sık olanlarından. Dört method bu işi görür:

const email = "ada@example.com";
console.log(email.includes("@"));      // true
console.log(email.startsWith("ada"));  // true
console.log(email.endsWith(".com"));   // true
console.log(email.indexOf("@"));       // 3
console.log(email.indexOf("#"));       // -1

includes, startsWith ve endsWith bir evet/hayır sorusuna cevap verir ve true ya da false döner. indexOf ise "nerede?" sorusunu cevaplar: aradığın parçanın başladığı index'i, bulamazsa -1 döner. Hepsi büyük-küçük harfe duyarlıdır; "Ada" ile "ada" onların gözünde iki ayrı şeydir.

Basit bir kural koy kendine: yalnızca var mı yok mu öğrenmek istiyorsan includes, konum gerçekten lazımsa indexOf. Bunun neden önemli olduğunu tuzaklar bölümünde göreceğiz.

Kesmek, Temizlemek, Biçimlendirmek

Bir string'in bir parçasını almak için slice(start, end) kullanılır. Başlangıç (start) dahildir, bitiş (end) dahil değildir; bitişi vermezsen sona kadar gider. Negatif değer verirsen sondan sayar:

const lang = "JavaScript";
console.log(lang.slice(0, 4));     // "Java"
console.log(lang.slice(4));        // "Script"
console.log(lang.slice(-6));       // "Script"
console.log(lang.slice(4, 0));     // ""
console.log(lang.substring(0, 4)); // "Java"
console.log(lang.substring(4, 0)); // "Java"

substring ilk bakışta slice'ın ikizi gibi duruyor ama iki farkı var: negatif sayıları sıfır kabul eder ve başlangıç bitişten büyükse ikisinin yerini kendiliğinden değiştirir. slice aynı durumda boş string döner. Davranışı daha tahmin edilebilir olduğu için günlük kodda slice'ı tercih et.

Kullanıcıdan gelen veride baştaki ve sondaki boşluklar başını ağrıtır. trim() ikisini birden temizler; yalnızca başı ya da sonu için trimStart() ve trimEnd() var. Tersi bir ihtiyaç da sık çıkar: bir metni belli bir uzunluğa tamamlamak. Bunun için ES2017 ile gelen padStart ve padEnd kullanılır:

console.log("   ada@example.com  ".trim());       // "ada@example.com"
console.log("7".padStart(3, "0"));                // "007"
console.log(`${"5".padStart(2, "0")}:30`);        // "05:30"
console.log("Invoice".padEnd(12, ".") + "$120");  // "Invoice.....$120"

Büyük ve küçük harf için de toUpperCase() ve toLowerCase() var. Türkçe metinlerle çalışırken bu ikisinde bir incelik saklı; onu tuzaklar bölümünde konuşacağız.

Değiştirmek ve Parçalara Bölmek

Metnin içindeki bir parçayı başka bir şeyle değiştirmek için replace kullanılır. Ama dikkat, sandığından daha tembeldir:

const text = "cat, dog, cat";
console.log(text.replace("cat", "bird"));    // "bird, dog, cat"
console.log(text.replaceAll("cat", "bird")); // "bird, dog, bird"

replace'e bir string verdiğinde yalnızca ilk eşleşmeyi değiştirir. Hepsini değiştirmek istiyorsan ES2021 ile gelen replaceAll var. Eskiden bunun için regular expression yazmak gerekiyordu; regex'i ilerleyen yazılarda ayrıca ele alacağız.

split ise bir string'i verdiğin ayırıcıdan bölüp parçaları bir array içinde döner:

console.log("apple,pear,banana".split(",")); // ["apple", "pear", "banana"]
console.log("code".split(""));               // ["c", "o", "d", "e"]
console.log("one two  three".split(" "));    // ["one", "two", "", "three"]

Son örnekteki boş string'e dikkat et. İki boşluğun arasında hiçbir şey olmadığı için JavaScript oraya boş bir parça koydu. Array'leri ilerleyen yazılarda detaylandıracağız; şimdilik split'in metni bir listeye çevirdiğini bilmen yeterli.

String'ler Değişmez

Baştaki soruya dönelim. Cevap "javascript". Ne ilk harf değişti ne de harfler büyüdü.

Sebebi, daha önce veri tipleri yazısında gördüğümüz bir kural: string bir primitive'dir ve primitive'ler değişmezdir (immutable). Bir string'i taşa kazınmış bir yazı gibi düşün. Okuyabilirsin, fotoğrafını çekip fotoğraf üzerinde istediğin oynamayı yapabilirsin, ama taşın kendisi değişmez. language[0] = "J" satırı taşa yeni bir harf kazımaya çalışmak gibidir; motor bu isteği sessizce yok sayar. Strict mode'da ise sessiz kalmaz, bir TypeError fırlatır.

toUpperCase() gibi method'lar da orijinal string'e dokunmaz. Yeni bir string üretip döner, yani fotoğrafı verir. O dönen değeri bir değişkene koymazsan, üretilen yeni string bir kenarda unutulur gider. Doğru kullanım şöyle:

let language = "javascript";
language = language[0].toUpperCase() + language.slice(1);
console.log(language); // "Javascript"

const loud = language.toUpperCase();
console.log(loud);     // "JAVASCRIPT"
console.log(language); // "Javascript"

Bu yazıda gördüğün method'ların hepsi için aynı kural geçerli: slice, trim, replace, padStart, toUpperCase. Hiçbiri elindeki string'i değiştirmez, hepsi yenisini verir. Sonucu kullanmak istiyorsan bir değişkene ata.

Sık Düşülen Tuzaklar

indexOf'u doğrudan koşula koymak

const file = "report.pdf";
if (file.indexOf("report")) {
  console.log("Report found");
} else {
  console.log("No report"); // çalışan satır bu!
}

"report" metnin en başında, yani index'i 0. Daha önce gördüğümüz falsy listesini hatırla: 0 falsy. Üstelik "bulunamadı" anlamına gelen -1 truthy. Yani bu koşul tam tersini söylüyor. Var mı yok mu sorusu için includes kullan.

Türkçe İ ve ı

console.log("istanbul".toUpperCase());              // "ISTANBUL"
console.log("istanbul".toLocaleUpperCase("tr-TR")); // "İSTANBUL"
console.log("IŞIK".toLowerCase());                  // "işik"
console.log("IŞIK".toLocaleLowerCase("tr-TR"));     // "ışık"

toUpperCase ve toLowerCase dilden bağımsız kurallarla çalışır: noktalı i'yi noktasız I'ya, I'yı da noktalı i'ye çevirir. Türkçede bu, "İSTANBUL" yerine "ISTANBUL" demek. Kullanıcıya gösterilecek Türkçe metinlerde toLocaleUpperCase("tr-TR") ve toLocaleLowerCase("tr-TR") kullan.

Emoji ve length

console.log("a".length);   // 1
console.log("😀".length);  // 2

let count = 0;
for (const char of "😀") {
  count++;
}
console.log(count);        // 1

JavaScript string'leri içeride UTF-16 kod birimleriyle tutar ve length karakterleri değil bu birimleri sayar. Harflerin çoğu tek birime sığar, ama emoji gibi bazı karakterler iki birim kaplar. Daha önce gördüğümüz for...of ise string'i gerçek karakterlerine göre gezer, bu yüzden 1 sayar. Ten rengi ya da aile emojisi gibi birleşik emojilerde bu bile yetmez. Şimdilik şunu aklında tut: length her zaman ekranda gördüğün karakter sayısı değildir.

Küçük Bir Deneme

★☆☆ const fullName = " ada lovelace "; ile başla. Önce boşlukları temizle, sonra split(" ") ile ikiye böl ve her parçanın ilk harfini büyütüp template literal ile "Ada Lovelace" çıktısını elde et.

★★☆ Çalıştırmadan önce tahmin et: "JavaScript".slice(-6, -3), "JavaScript".substring(-6, 3) ve "JavaScript".at(-11) ne döner? Sonra konsolda kontrol et ve yanıldığın yer varsa nedenini bul.

★★☆ Telefon numarasını maskeleyen bir satır yaz: "05321234567" verildiğinde "*******4567" çıksın. slice ile padStart ikilisi işini görür; uzunluğu elle yazmak yerine length kullan.

★★★ Bir cümledeki kelime sayısını bul: " JavaScript is fun " için cevap 3 olmalı. trim ve split seni bir yere kadar götürür; birden fazla boşluk yüzünden çıkan boş parçaları daha önce gördüğümüz bir döngüyle ayıklaman gerekecek.

Bu yazıda metinleri kesip biçtik, ama formdan gelen "28" de bir string ve onunla hesap yapamazsın. Bir sonraki yazıda number tipine ve Math object'ine geçiyoruz: string'i sayıya nasıl çevireceğimizi, rastgele sayı üretmeyi ve 0.1 + 0.2'nin neden tam olarak 0.3 etmediğini göreceğiz.