Rest ve Spread

Modern JavaScript'in en kullanışlı sözdizimlerinden ikisini öğrenme zamanı: rest ve spread.

Felsefi olarak, rest ve spread birbirinin tersini yapar:

Her iki işlem de aynı sözdizimini kullanır: üç nokta (...). Teknik olarak bir operatör değildir ama kesinlikle öyle hissettiriyor!

Tarayıcı Desteği

Rest parametreleri ve spread sözdizimi tüm modern tarayıcılarda desteklenir. Internet Explorer gibi eski tarayıcıları desteklemeniz gerekiyorsa, Babel gibi bir derleyici kullandığınız sürece endişelenmenize gerek yok — kodunuz otomatik olarak uyumlu sözdizimine çevrilir.


Rest Parametreleri

Değişken sayıda parametre alan bir fonksiyon yazmak istediğimizi düşünelim. Kaç sayı verilirse verilsin hepsini toplayan bir fonksiyon:

addNums(1, 1); // 2
addNums(1, 2, 3, 4); // 10
addNums(1, 10, 100, 1000); // 1111

Bunu rest parametreleri ile yapabiliriz:

function addNums(...nums) {
  let sum = 0;
  nums.forEach((num) => {
    sum += num;
  });
  return sum;
}

nums, gelen tüm argümanları bir dizi içinde toplayan tek bir parametredir:

function logArgs(...args) {
  console.log(args);
}

logArgs(1, 2, "merhaba!");
// -> [1, 2, 'merhaba!']

Herhangi bir parametre gibi istediğimiz ismi verebiliriz. ...rest yaygın bir tercih olsa da ...tumDegerler de olabilir.

Bazen bir veya daha fazla sabit parametreyle birlikte değişken sayıda parametre de kullanmak isteriz:

function ilkiHaricHepsini(first, ...rest) {
  return rest;
}

ilkiHaricHepsini(1, 2, 3, 4);
// -> [2, 3, 4]

Ancak birkaç kural var: yalnızca tek bir rest parametresi kullanılabilir ve her zaman en sona yazılmalıdır:

// ❌ Geçersiz — iki rest parametresi
function yanlis(...ilkRest, ...rest) {}

// ❌ Geçersiz — rest parametresi sonda değil
function yanlis2(...rest, ikinci, ucuncu) {}

Spread Sözdizimi

Spread sözdizimi, rest parametrelerinin tam tersidir. Bir dizi veriyi tek tek argümanlara açar.

function tarihOlustur(yil, ay, gun) {
  return new Date(yil, ay, gun);
}

const tarihBilgisi = [2024, 5, 15];

tarihOlustur(...tarihBilgisi);
// Şuna eşdeğer:
// tarihOlustur(2024, 5, 15)

...tarihBilgisi, diziyi üç ayrı argümana açar. Bunu elle yazmak zorunda kalsaydık:

tarihOlustur(tarihBilgisi[0], tarihBilgisi[1], tarihBilgisi[2]);

Spread sözdiziminin çok kullanışlı olduğu durumlar var.

Diziyi Kopyalamak

const dizi = [1, 2, 3, 4];

const kopya = [...dizi];

console.log(kopya); // [1, 2, 3, 4]
console.log(dizi === kopya); // false — farklı diziler!

...dizi sözdizimi değerleri tek tek açar. Köşeli parantez ([]) içine aldığımız için yeni bir dizi oluşturulur. Bu, bir önceki derste öğrendiğimiz immutable güncelleme için de kullandığımız yöntemdir!

İki Diziyi Birleştirmek

const sayilar1 = [1, 2, 3];
const sayilar2 = [4, 5, 6];

const hepsi = [...sayilar1, ...sayilar2];
console.log(hepsi); // [1, 2, 3, 4, 5, 6]

Nesnelerde Spread Kullanımı

Spread sözdizimi başlangıçta yalnızca dizilerle çalışıyordu, ama artık nesnelerle de kullanabiliyoruz!

Nesneyi Kopyalamak

const orijinal = {
  enlem: 1.234,
  boylam: 4.321,
};

const kopya = { ...orijinal };

Mevcut Nesneyi Genişletmek

const ortakOzellikler = {
  tur: "insan",
  konum: "dünya",
};

const kisi1 = {
  ...ortakOzellikler,
  isim: "Tina",
  gozRengi: "yeşil",
};

const kisi2 = {
  ...ortakOzellikler,
  isim: "James",
  gozRengi: "kahverengi",
};

İki Nesneyi Birleştirmek

const nesne1 = { a: 1 };
const nesne2 = { b: 2 };

const birlestir = { ...nesne1, ...nesne2 };
console.log(birlestir); // { a: 1, b: 2 }

Çakışmalar Nasıl Çözülür?

Dizilerde çakışma sorunu yoktur — tüm elemanlar yeni diziye dahil edilir:

const dizi1 = [1, 2, 3];
const dizi2 = [3, 4, 5];

const birlestir = [...dizi1, ...dizi2];
console.log(birlestir);
// -> [1, 2, 3, 3, 4, 5]  — 6 eleman, 3 iki kez var

Nesnelerde ise aynı anahtar birden fazla kez tanımlanabilir. Kural basit: sonra gelen önce geleni ezer.

const nesne1 = {
  renk: "kırmızı",
  zIndex: 5,
};

const nesne2 = {
  fontSize: "2rem",
  zIndex: 10,
};

const birlestir = { ...nesne1, ...nesne2 };
console.log(birlestir);
/*
  {
    renk: 'kırmızı',
    fontSize: '2rem',
    zIndex: 10,  <- nesne2'den geldi, nesne1'i ezdi
  }
*/

Varsayılan Değerler Tanımlamak

Bu mantığı kullanarak "üzerine yazılabilen varsayılan değerler" oluşturabiliriz:

const varsayilanlar = {
  tur: "kullanici",
  sifre: "12345",
};

const kullanici1 = {
  ...varsayilanlar,
  kullaniciAdi: "helloWorld",
  // Şifre tanımlanmadı → varsayılan '12345' kullanılır
};

const kullanici2 = {
  ...varsayilanlar,
  kullaniciAdi: "cheeseCrackers",
  sifre: "cok-guclu-sifre-123", // Varsayılanı ezer
};

⚠️ Dikkat: Varsayılanları önce spread etmelisiniz, sonra değil! Aksi takdirde mantık tersine döner:

// ❌ Yanlış sıra — varsayılanlar her şeyi ezer!
const yanlis = {
  kullaniciAdi: "misterSkydive",
  sifre: "ozel-sifrem",
  ...varsayilanlar, // Bu, üstteki sifre'yi '12345' ile ezdi!
};

console.log(yanlis.sifre); // '12345' — istemediğimiz sonuç!
// ✅ Doğru sıra — varsayılanlar üzerine yazılabilir
const dogru = {
  ...varsayilanlar,
  kullaniciAdi: "misterSkydive",
  sifre: "ozel-sifrem", // Varsayılanı başarıyla eziyor
};

console.log(dogru.sifre); // 'ozel-sifrem' — istediğimiz sonuç!

Özet

RestSpread
Ne yapar?Değerleri toplarDeğerleri açar
Nerede kullanılır?Fonksiyon parametrelerindeDizi/nesne oluştururken, fonksiyon çağrılarında
Sözdizimi...params...dizi veya ...nesne

Rest ve spread sözdizimi, React geliştirmede her yerde karşımıza çıkacak. Özellikle state güncellemelerinde immutable kopya oluştururken [...dizi] ve {...nesne} kalıplarını sürekli kullanacağız.