Template literal, ters tırnak (`) ile yazılan bir metin değişmezidir ve üç şey sağlar: ${ifade} ile metnin içine değer yerleştirme, kaynak kodda doğrudan çok satırlı metin yazma ve metni bir fonksiyona işletmeyi sağlayan etiketli şablonlar (tagged templates). String.raw ise kaçış dizilerini işlemeden ham metni veren yerleşik etikettir.

Bu yazıda her özelliği Node.js 24 ile çalıştırılmış örneklerle anlatıyoruz. Metinler üzerinde arama, kesme veya değiştirme gibi işlemler için string metotları yazısına bakabilirsiniz.

Değer yerleştirme (interpolation)

Değer yerleştirme (interpolation)

${...} içine değişken, aritmetik işlem, fonksiyon çağrısı veya üçlü operatör gibi herhangi bir ifade yazabilirsiniz. Sonuç metne dönüştürülüp yerine konur. Bu, + ile birleştirmekten çok daha okunaklıdır.

javascript
const ad = "Deniz";
const adet = 3;
const fiyat = 49.9;

console.log("Merhaba " + ad + ", toplam: " + adet * fiyat + " TL");
// Merhaba Deniz, toplam: 149.7 TL
console.log(`Merhaba ${ad}, toplam: ${(adet * fiyat).toFixed(2)} TL`);
// Merhaba Deniz, toplam: 149.70 TL

İkinci satırda hem hesaplama hem de toFixed(2) ile iki basamaklı biçimlendirme doğrudan yer tutucunun içinde yapıldı; ek parantez ve + işaretleri ortadan kalktı. Ondalık sayıların biçimlendirilmesi ve kayan nokta ayrıntıları için sayılar yazısına bakabilirsiniz.

+ ile küçük ama gerçek bir fark

MDN'in belirttiği ince bir ayrım var: template literal içindeki ifade doğrudan metne çevrilir, + operatörü ise önce değeri ilkel (primitive) bir değere dönüştürür. toString ve valueOf farklı sonuç veren bir nesnede bu fark görünür hâle gelir:

javascript
const nesne = {
  toString() {
    return "metin";
  },
  valueOf() {
    return 42;
  },
};

console.log(`${nesne}`); // metin
console.log("" + nesne); // 42

Çok satırlı metin

Kaynak koddaki satır sonları template literal'ın parçası olur; \n yazmanız gerekmez. Uzun bir metni yalnızca kaynak kodda bölmek, çıktıda tek satır tutmak istiyorsanız satır sonunu ters eğik çizgiyle (\) kaçırın. Ters tırnak ve ${ karakterlerini metin olarak yazmak için de önlerine ters eğik çizgi koyulur.

javascript
const iki = `Birinci satır
İkinci satır`;
console.log(iki.split("\n").length); // 2

const tek = `Bu cümle \
tek satırdır`;
console.log(tek); // Bu cümle tek satırdır

console.log(`\`` === "`", `\${1}` === "${1}"); // true true

İç içe şablonlar

${...} içinde yeni bir template literal açabilirsiniz. Koşula göre değişen küçük parçalar için bu, dışarıda geçici değişken tanımlamaktan daha derli topludur.

javascript
const urunler = [
  { ad: "Çay", stok: 0 },
  { ad: "Kahve", stok: 12 },
];

const liste = urunler
  .map((u) => `${u.ad}: ${u.stok > 0 ? `${u.stok} adet` : "tükendi"}`)
  .join("\n");
console.log(liste);
// Çay: tükendi
// Kahve: 12 adet

Etiketli şablonlar (tagged templates)

Template literal'ın önüne bir fonksiyon adı yazdığınızda (etiket`...`) metin doğrudan oluşturulmaz; bunun yerine fonksiyon çağrılır. İlk argüman sabit metin parçalarından oluşan bir dizidir, kalan argümanlar ise ${} içindeki ifadelerin değerleridir. Metin parçası dizisinin uzunluğu her zaman ifade sayısının bir fazlasıdır. Fonksiyon ne döndürürse ifadenin sonucu o olur; metin döndürmek zorunda değildir.

javascript
function vurgula(parcalar, ...degerler) {
  return parcalar.reduce(
    (sonuc, parca, i) =>
      sonuc + parca + (i < degerler.length ? `[${degerler[i]}]` : ""),
    "",
  );
}

const oyuncu = "Mert";
const puan = 87;
console.log(vurgula`Oyuncu ${oyuncu} ${puan} puan aldı.`);
// Oyuncu [Mert] [87] puan aldı.

function incele(parcalar, ...degerler) {
  console.log(parcalar, degerler);
}
incele`a${1}b${2}`; // [ 'a', 'b', '' ] [ 1, 2 ]

reduce ve rest parametresi burada metin parçalarını değerlerle sırayla birleştirmek için kullanıldı; bu yapıların ayrıntısı dizi metotları ve rest parametreler yazılarında.

Metin parçası dizisi ve onun raw özelliği dondurulmuştur (frozen). Ayrıca aynı etiketli şablon her değerlendirildiğinde fonksiyona aynı dizi nesnesi verilir; bu da sonuçları dizinin kimliğine göre önbelleğe almayı mümkün kılar.

javascript
const gorulen = [];
function etiket(parcalar) {
  gorulen.push(parcalar);
  return {};
}
const calistir = () => etiket`Merhaba ${"dünya"}!`;

console.log(calistir() === calistir()); // false
console.log(gorulen[0] === gorulen[1]); // true
console.log(Object.isFrozen(gorulen[0]), Object.isFrozen(gorulen[0].raw)); // true true

Pratik örnek: HTML kaçışı yapan etiket

Kullanıcıdan gelen bir değeri HTML içine yerleştirirken özel karakterleri kaçırmanız gerekir; aksi halde araya kod sızabilir. Etiketli şablon, sabit kısımlara dokunmadan yalnızca dışarıdan gelen değerleri işlemeye çok uygundur.

javascript
function guvenliHtml(parcalar, ...degerler) {
  const kacir = (s) =>
    String(s)
      .replaceAll("&", "&amp;")
      .replaceAll("<", "&lt;")
      .replaceAll(">", "&gt;")
      .replaceAll('"', "&quot;");
  return String.raw({ raw: parcalar }, ...degerler.map(kacir));
}

const yorum = '<script>alert("x")</script>';
console.log(guvenliHtml`<p>${yorum}</p>`);
// <p>&lt;script&gt;alert(&quot;x&quot;)&lt;/script&gt;</p>

Burada String.raw({ raw: parcalar }, ...) çağrısı, MDN'in "kimlik etiketi" için önerdiği yöntemi kullanıyor: işlenmiş (cooked) metin parçalarını ham parça gibi vererek normal bir template literal ile aynı sonucu üretir. Bu örnek yalnızca kavramı gösterir; üretim ortamında bağlama göre (öznitelik, URL, betik) doğru kaçış kurallarını uygulayan, denenmiş bir kütüphane veya çatının kendi şablon motoru tercih edilmelidir.

String.raw: kaçış dizilerini işlemeyen ham metin

String.raw, dilin tek yerleşik template etiketidir. ${} yerleştirmelerini yapar ama \n, \t gibi kaçış dizilerini çözmez; ters eğik çizgiler olduğu gibi kalır. Windows dosya yolları ve düzenli ifadeler gibi çok sayıda ters eğik çizgi içeren metinlerde işinizi kolaylaştırır.

javascript
const yol = String.raw`C:\Users\deniz\belgeler`;
console.log(yol); // C:\Users\deniz\belgeler

console.log(`a\nb`.length); // 3 (\n tek karakter: satır sonu)
console.log(String.raw`a\nb`.length); // 4 (\ ve n ayrı karakterler)
console.log(String.raw`Toplam: ${2 + 3}\n`); // Toplam: 5\n

const desen = new RegExp(String.raw`^\d{3}-\d{2}$`);
console.log(desen.test("123-45")); // true

Normal bir metinde aynı deseni "^\\d{3}-\\d{2}$" diye çift ters eğik çizgiyle yazmanız gerekirdi. Düzenli ifadelerin kendisi için regex başlangıç rehberine bakabilirsiniz.

Etiket fonksiyonlarında aynı ham metne parcalar.raw üzerinden erişilir. Etiketli şablonlarda geçersiz kaçış dizileri sözdizimi hatası vermez: işlenmiş dizide ilgili parça undefined olur, ham hâli ise korunur. Etiketsiz bir template literal'da aynı metin SyntaxError verir.

javascript
function goster(parcalar) {
  console.log("İşlenmiş:", parcalar[0]);
  console.log("Ham:", parcalar.raw[0]);
}
goster`\unicode`;
// İşlenmiş: undefined
// Ham: \unicode

Yaygın hatalar

Yaygın hatalar: Tırnak yerine ters tırnak unutmak, Etiketsiz şablonları art arda yazmak, Optional chaining ile etiket, Kullanıcı girdisini doğrudan HTML veya SQL'e yerleştirmek
  • Tırnak yerine ters tırnak unutmak: "Merhaba ${ad}" çift tırnakla yazıldığında yerleştirme yapılmaz; metin olduğu gibi çıkar.
  • Etiketsiz şablonları art arda yazmak: `Hello``World` ifadesi, ilk metni fonksiyon gibi çağırmaya çalışır ve TypeError verir.
  • Optional chaining ile etiket: String?.raw`...` ve String.raw?.`...` sözdizimi hatasıdır.
  • Kullanıcı girdisini doğrudan HTML veya SQL'e yerleştirmek: Template literal yalnızca metin birleştirir, güvenlik sağlamaz. Kaçış veya parametreli sorgu kullanın.
  • String.raw sonunda tek ters eğik çizgi: Son karakter olarak yazılan \ kapanış ters tırnağını kaçırır ve metin kapanmaz.
javascript
try {
  console.log(`Hello``World`);
} catch (e) {
  console.log(e.constructor.name, e.message); // TypeError "Hello" is not a function
}

Sık Sorulan Sorular

Template literal ile template string aynı şey mi?

Evet, gayriresmî olarak "template string" de denir. Ancak etiketli şablonların sonucu metin olmak zorunda olmadığı için MDN "template literal" adını kullanır.

Template literal JSON için kullanılabilir mi?

Elle JSON metni üretmek yerine JSON.stringify() kullanın; kaçış ve tırnak kurallarını doğru uygular. Ayrıntılar JSON.parse ve stringify yazısında.

String.raw ne zaman kullanılmamalı?

MDN, String.raw fonksiyonunu doğrudan "kimlik etiketi" olarak kullanmamayı önerir, çünkü kaçış dizilerini işlemez ve normal metinden farklı sonuç verir. Kimlik etiketi gerekiyorsa yukarıdaki String.raw({ raw: parcalar }, ...) kalıbını kullanın.

${} içine nesne koyarsam ne olur?

Nesne metne dönüştürülür; düz nesneler için sonuç [object Object] olur. Okunabilir çıktı için JSON.stringify() kullanın veya nesneye kendi toString() metodunu yazın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: JavaScript öğrenme rehberi · String metotları · JSON.parse ve JSON.stringify · Regex başlangıç rehberi · Dizi metotları: map, filter, reduce · Optional chaining ve nullish