Mert Özen Her Satırda Daha İleri
Frontend JavaScript Orta — #01

JavaScript Modern Syntax: Destructuring, Spread/Rest, Optional Chaining ve Mantıksal Atama

Mert Özen 3 Kas 2026 9 dk 4 görüntülenme
JavaScript Modern Syntax: Destructuring, Spread/Rest, Optional Chaining ve Mantıksal Atama

Array ve object destructuring'i, spread ve rest farkını, kısa property ve computed property yazımını, ?. ile ?? kullanımını ve ||=, &&=, ??= operatörlerini hangi ES sürümüyle geldikleriyle birlikte öğreniyoruz.

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

Önce Tahmin Et

Temel seride değişkenlerden fonksiyonlara, object'lerden array'lere, DOM ve event'lerden formlara, BOM, Date ve RegEx'e kadar dilin ve tarayıcının yapı taşlarını gördük. Orta seride bu temelin üstüne modern syntax'ı, scope, closure ve this gibi dilin derin kavramlarını, class'ları, asenkron programlamayı ve tarayıcının daha gelişmiş API'lerini koyacağız. İlk durak, bugün açtığın hemen her JavaScript dosyasında karşına çıkan kısa yazımlar.

Isınmak için şu üç satıra bak:

const { a = 1, b = 2 } = { a: undefined, b: null };
console.log(a);
console.log(b);

Bu kod konsola ne yazdırır? Okumaya devam etmeden önce bir tahmin yap. Cevabı object destructuring'ini anlattıktan sonra vereceğiz.

Array Destructuring: Koliden Parça Parça Çıkarmak

Bir taşınma kolisi düşün. İçinden eşyaları tek tek çıkarıp her birini ayrı bir rafa koyuyorsun. Destructuring tam olarak bu: bir array'in ya da object'in içindeki değerleri tek satırda ayrı değişkenlere dağıtmak. ES2015 ile gelen bu syntax'tan önce aynı işi index'lerle tek tek yapardık.

Bunu tarayıcının konsoluna yapıştırıp dene:

const colors = ["red", "green", "blue"];

// Eski yol
const firstColor = colors[0];
const secondColor = colors[1];

// Destructuring
const [red, green, blue] = colors;
console.log(red, green, blue); // red green blue

Sol taraftaki köşeli parantez burada yeni bir array oluşturmuyor; "sağdaki array'in element'lerini sırasıyla şu isimlere bağla" diyor. JavaScript engine'i sağ tarafa bakar, ilk element'i ilk isme, ikinciyi ikinci isme verir. Array'de element eksikse karşılık gelen değişken undefined olur, fazlası varsa sessizce yok sayılır.

Atlamak istediğin bir element varsa virgülle yerini boş bırakırsın. İki değişkenin değerini geçici bir değişken kullanmadan takas etmek de destructuring'in en sevilen numaralarından biridir:

const [, , third] = ["a", "b", "c"];
console.log(third); // "c"

let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1

Object Destructuring: Varsayılan Değer ve Yeniden Adlandırma

Object'lerde sıra değil isim önemlidir. Süslü parantezin içine yazdığın isim, object'teki property'nin adıyla eşleşir:

const user = { firstName: "Ada", city: "London", age: 31 };

const { firstName, city } = user;
console.log(firstName, city); // Ada London

Değişkene farklı bir isim vermek istersen iki nokta kullanırsın: { city: town } "city property'sini al, town adlı değişkene koy" demektir. Eşittir işaretiyle de varsayılan değer verirsin. İkisi bir arada da kullanılabilir:

const { city: town, country = "UK", age: years = 18 } = user;
console.log(town, country, years); // London UK 31

Şimdi baştaki soruya dönelim. Cevap 1 ve null. Varsayılan değer yalnızca karşılık gelen değer tam olarak undefined olduğunda devreye girer. a undefined olduğu için varsayılan 1 kullanıldı. b ise null, yani "bilerek boş bırakılmış" bir değer; engine açısından ortada bir değer var ve varsayılana gerek görmüyor. Aynı kural daha önce gördüğümüz varsayılan parametrelerde de geçerli.

Destructuring'in en çok işe yaradığı yer fonksiyon parametreleri. Çok sayıda ayarı olan bir fonksiyona sıralı argümanlar yerine tek bir object verirsin, fonksiyon da ihtiyacı olanları parametre listesinde ayıklar:

function createButton({ label, color = "gray", size = "md" } = {}) {
  return `${label} (${color}, ${size})`;
}

console.log(createButton({ label: "Save", color: "green" })); // Save (green, md)
console.log(createButton()); // undefined (gray, md)

Sondaki = {} küçük ama önemli bir detay. Fonksiyon hiç argüman verilmeden çağrılırsa undefined'ı parçalamaya çalışır ve hata verir; boş object varsayılanı bunu önler. Argümanların sırasını ezberlemek zorunda kalmamak da cabası. İç içe object'lerden de aynı şekilde değer çekebilirsin, örneğin const { address: { zip } } = order;. Ama iki seviyeden derine indiğinde okunabilirlik hızla düşer, abartmamak lazım.

Spread ve Rest: Aynı Üç Nokta, İki Ayrı İş

Daha önce array'leri ve object'leri kopyalarken üç noktayı (...) kullanmıştık. Bu üç nokta bulunduğu yere göre birbirinin tersi iki iş yapar. Bir değer beklenen yerde spread olur ve element'leri dışarı saçar. Bir değişken ya da parametre tanımlanan yerde rest olur ve geri kalanları toplar. Bir torba düşün: spread torbayı masaya boşaltır, rest masada kalanları torbaya süpürür.

// Spread: aç ve saç
const base = [1, 2, 3];
const more = [0, ...base, 4];           // [0, 1, 2, 3, 4]
console.log(Math.max(...base));         // 3

const defaults = { theme: "light", lang: "en" };
const prefs = { ...defaults, theme: "dark" };
console.log(prefs);                     // { theme: "dark", lang: "en" }

// Rest: topla
const [head, ...tail] = [10, 20, 30];   // head: 10, tail: [20, 30]
const { theme, ...others } = prefs;     // theme: "dark", others: { lang: "en" }

function sum(...numbers) {
  let total = 0;
  for (const n of numbers) total += n;
  return total;
}
console.log(sum(1, 2, 3, 4));           // 10

Object spread'inde sıra önemlidir: aynı isimde iki property varsa sonda yazılan kazanır. Bu yüzden önce varsayılanları, sonra kullanıcının seçimlerini yazarsın. Array'lerde spread ve rest ES2015 ile geldi; object'ler için aynı syntax ES2018'de eklendi.

Rest parametresi her zaman en sonda olmalı ve bir fonksiyonda yalnızca bir tane olabilir. Eski kodlarda gördüğün arguments object'inin modern karşılığı budur, üstelik gerçek bir array'dir. Bir de şunu unutma: spread sığ (shallow) kopya yapar. İç içe bir object'i kopyaladığında içteki object iki kopya arasında ortak kalır; derin kopya için daha önce gördüğümüz structuredClone var.

Kısa Yazımlar: Property, Method ve Computed Property

Object oluştururken değişkenin adı ile property'nin adı aynıysa ikisini ayrı ayrı yazmana gerek yok. Method'larda da function kelimesi atılabilir. Köşeli parantezle ise property adını çalışma anında hesaplayabilirsin; buna computed property denir. Üçü de ES2015'in hediyesi:

const title = "Keyboard";
const price = 125;
const field = "stock";

const product = {
  title,                 // title: title
  price,                 // price: price
  [field]: 40,           // stock: 40
  [`${field}Alert`]: 5,  // stockAlert: 5
  describe() {           // describe: function () { ... }
    return `${this.title} - ${this.price} USD`;
  }
};

console.log(product.stock, product.stockAlert); // 40 5
console.log(product.describe());                 // Keyboard - 125 USD

Computed property özellikle alan adının önceden bilinmediği durumlarda işe yarar. Bir form alanını object'e çevirirken { [input.name]: input.value } yazarsan hangi input gelirse gelsin doğru key üretilir. Method içindeki this'e burada sadece göz kırpıyoruz; nasıl belirlendiğini ilerleyen yazılarda ayrıntılı göreceğiz.

Optional Chaining ve ??: Güvenli Erişim

İç içe veriyle çalışırken en sık gördüğün hata büyük ihtimalle Cannot read properties of undefined olur. Zincirin ortasındaki bir halka yoksa ondan sonrasına erişemezsin. ES2020 ile gelen optional chaining (?.) her adımda "burada bir şey var mı?" diye sorar. Yoksa hata fırlatmak yerine undefined döner ve zincirin geri kalanını hiç çalıştırmaz. Buna kısa devre denir.

const order = {
  id: 7,
  customer: { name: "Grace", address: null },
  items: [{ title: "Book" }]
};

console.log(order.customer.address?.city);  // undefined
console.log(order.items?.[0]?.title);       // "Book"
console.log(order.coupon?.code);            // undefined
console.log(order.customer.notify?.());     // undefined, böyle bir fonksiyon yok

Üç biçimi var: property için ?., köşeli parantezle erişim için ?.[], fonksiyon çağırmak için ?.(). Sonuna daha önce gördüğümüz ?? operatörünü eklediğinde güvenli erişim ile varsayılan değer tek satırda birleşir. ?? da aynı yıl, ES2020 ile geldi:

const shipCity = order.customer.address?.city ?? "No address";
console.log(shipCity); // "No address"

Bir uyarı: ?.'yı her noktaya serpiştirmek hataları saklar. Bir verinin bazen olmaması normalse kullan. Olmaması bir hata anlamına geliyorsa zinciri düz bırak ki sorunu erkenden, gerçekleştiği yerde gör.

Mantıksal Atama Operatörleri

ES2021 ile gelen ||=, &&= ve ??=, sık yazdığımız "değer yoksa şunu ata" kalıbını kısaltır. Her biri, karşılık gelen operatörün kısa devre kuralıyla çalışır:

const config = { retries: 0, label: "", timeout: undefined, token: "abc" };

config.retries ||= 3;          // 0 falsy, 3 olur
config.label ??= "Untitled";  // "" null/undefined değil, olduğu gibi kalır
config.timeout ??= 5000;       // undefined, 5000 olur
config.token &&= config.token.toUpperCase(); // yalnızca varsa dönüştür: "ABC"

console.log(config);
// { retries: 3, label: "", timeout: 5000, token: "ABC" }

a ||= b, a = a || b ile değil, a || (a = b) ile aynıdır. Fark ince ama gerçek: sol taraf zaten truthy ise atama hiç yapılmaz. Atamanın kendisi bir iş yaptığında, örneğin bir DOM property'sini değiştirdiğinde, bu fark önem kazanır. Hangisini seçeceğin neyi "boş" saydığına bağlı. 0, "" ve false senin için geçerli değerlerse ??= kullan; o yalnızca null ve undefined'a tepki verir.

Hangi Özellik Hangi Sürümle Geldi?

Bu yazıdaki her şey bugün tüm güncel tarayıcılarda çalışıyor, ama eski bir kod tabanında ya da eski bir yazıda karşına çıktığında tarihini bilmek işine yarar. Array ve object destructuring, varsayılan değerler, array spread'i, rest parametresi, kısa property ve method yazımı ve computed property ES2015 ile geldi. Object'lerde rest ve spread ES2018'de eklendi. Optional chaining ve ?? ES2020'nin, mantıksal atama operatörleri ise ES2021'in ürünü.

Gördüğün gibi modern syntax'ın büyük kısmı ES2015'te geldi, sonraki yıllar küçük ama her gün kullanılan parçalar ekledi. ES2026'da yeni bir syntax yok; bu sürümün yenilikleri Math.sumPrecise ya da Map.prototype.getOrInsert gibi yeni fonksiyon ve method'lar. Onlara da sırası geldikçe değineceğiz.

Sık Düşülen Tuzaklar

İlki, satır başında süslü parantezle atama. Önceden tanımlanmış değişkenlere object destructuring'iyle değer verirken satırı { ile başlatırsan engine bunu bir kod bloğu sanar ve SyntaxError verir. Çözüm, ifadenin tamamını parantez içine almak:

let width, height;
({ width, height } = { width: 800, height: 600 });
console.log(width, height); // 800 600

İkincisi, olmayan bir değeri parçalamak. const { id } = null; doğrudan TypeError verir. Veri bir API'den ya da kullanıcıdan geliyorsa = {} varsayılanını ya da ?? {} korumasını alışkanlık hâline getir.

Üçüncüsü, || ile ??'yu karıştırmak. Kullanıcı ses seviyesini bilerek 0 yaptıysa volume || 50 bunu ezer ve 50 yapar. Sayı, boş string ya da boolean olabilen ayarlarda neredeyse her zaman ?? istersin. Bu ikisini parantezsiz yan yana da yazamazsın: a || b ?? c doğrudan SyntaxError verir. Dil, hangisinin önce çalışacağını tahmin etmeni istemiyor; parantezle niyetini açıkça yaz.

Dördüncüsü, spread'i derin kopya sanmak. { ...order } ile aldığın kopyada customer hâlâ aynı object'i gösterir. Kopyada müşterinin adını değiştirirsen orijinaldeki de değişir.

Küçük Bir Deneme

★☆☆ let ile [3, 7] array'ini destructuring kullanarak px ve py değişkenlerine aç. Sonra tek satırda yerlerini değiştir ve konsola yazdır.

★★☆ Çalıştırmadan önce çıktıyı tahmin et: const { level = "medium", score = 10 } = { level: null, score: 0 }; console.log(level, score); Ardından aynı soruyu null ?? "medium" ve 0 || 10 için düşün.

★★☆ function formatUser({ name, email, role = "member" } = {}) imzasıyla bir fonksiyon yaz ve kullanıcının adını ve rolünü içeren bir cümle döndürsün. Argümansız çağrıldığında da hata vermesin.

★★★ setDefault(obj, key, value) adında iki ayrı sürüm yaz. İlki ??= ile object'i yerinde değiştirsin. İkincisi spread ve computed property ile hiçbir şeye dokunmadan yeni bir object döndürsün. Aynı object'le ikisini de çağır ve orijinal object'in hangi durumda değiştiğini konsolda göster.

Bu yazıda array'leri ve object'leri parçalayıp yeniden kurmayı gördük. Sırada array'lerle çalışmanın asıl gücü var: map, filter ve reduce başta olmak üzere array method'larına derinlemesine bakacağız. Destructuring ve spread orada da sık sık elimizin altında olacak.