Spread Operator ile Nesneyi Başka Bir Nesneye Genişletmek
JavaScript'te object spread syntax'ı, yani bir nesnenin önüne konan üç nokta (...), o nesnenin kendi enumerable property'lerini içinde bulunduğu nesne literaline kopyalar. Bir nesneyi klonlamak, birleştirmek ya da orijinaline dokunmadan değiştirilmiş bir kopyasını üretmek için bildiğim en kısa yol.
copy nesnesi, user ile aynı anahtarlara ve değerlere sahip — ama farklı bir nesne. Birini değiştirmek diğerini etkilemez; en azından üst seviyede. Bu ayrıntıya birazdan tekrar döneceğiz.
Zihinsel model şu: { ...obj } aslında "obj nesnesinin özelliklerini bu yeni nesne literalinin içine boşalt" demek. Spread'in yanına ne yazarsan, sonuca dahil oluyor.
Tek Adımda Klonlama ve Üzerine Yazma
En sık karşılaşacağın kalıp, bir nesneyi spread ile açıp ardından birkaç özelliği eklemek ya da üzerine yazmak. Sonra gelen anahtar kazandığı için önce spread edip sonra override etmelisin:
user hiç dokunulmadan kalıyor. updated ise role alanı değiştirilmiş yeni bir nesne. Bu immutable update (değiştirmeden güncelleme) deseni modern JavaScript'te her yerde karşınıza çıkar — React state güncellemeleri, Redux reducer'ları, kısaca yerinde mutasyondan kaçınmak isteyen her kod parçasında.
Sırayı ters çevirdiğinizde ise tam tersi davranışı elde edersiniz:
Burada önce role: "guest" geliyor, dolayısıyla user.role onu ezip üzerine yazıyor. Spread ile gelen nesnenin varsayılanları ezmesini istediğinizde çok işe yarar.
JavaScript Nesne Birleştirme
İki (veya daha fazla) nesneyi yeni bir nesne literali içine spread ederek birleştirebilirsiniz. Çakışan anahtarlarda sonraki nesneler öncekileri ezer:
theme ve fontSize değerleri userPrefs nesnesinden geliyor; debug ise defaults içinden geçip geliyor. Üç nesne mi, dört nesne mi — fark etmez, kural aynı: soldan sağa okunur, en son yazılan kazanır.
Bu yaklaşım, Object.assign({}, defaults, userPrefs) kullanımının modern karşılığıdır. İkisi de aynı işi yapar ama spread versiyonu hem daha okunabilir hem de sizi Object.assign(defaults, userPrefs) gibi sık yapılan bir hataya düşürmez — çünkü bu ikincisi defaults nesnesini doğrudan değiştirir (mutate eder).
Spread Operatörü Shallow Copy Yapar
İşte insanların çoğunu tökezleten nokta burası. Spread operatörü, nesnenin yalnızca üst seviye özelliklerini kopyalar. Bir özelliğin değeri başka bir nesne ya da diziyse, içeriği değil referansı kopyalanır.
copy.address.city değerini değiştirdiğimizde user.address.city de değişti — çünkü iki nesne de aynı address nesnesini paylaşıyor. Spread bize sadece yeni bir dış kabuk verdi.
İç içe bir alanı değiştirmen gerekiyorsa, değiştirmek istediğin her katmanı tek tek spread'lemelisin:
Gerçekten derin bir kopya (deep copy) almak istiyorsan structuredClone(obj) tam sana göre. İç içe nesneler, diziler, Date, Map ve Set — hepsiyle sorunsuz çalışır. Üstelik tüm modern JavaScript ortamlarında yerleşik olarak gelir.
Spread vs Rest operatörü
Aynı üç noktayı paylaşırlar ama işleri tam tersidir. Spread açar, rest ise toplar.
Pratik bir kural: Eğer ... bir = işaretinden önce geliyorsa (destructuring tarafında), bu rest'tir. Ama bir object veya array literal'ın içinde, yani atamanın değer tarafında kullanılıyorsa spread'dir.
Immutable Şekilde Property Silme
Rest destructuring'i spread ile birleştirince, bir nesnenin belirli bir key'i çıkarılmış kopyasını çok temiz bir şekilde üretebilirsin — ne delete'e ihtiyacın var, ne de orijinal nesneyi mutate etmene:
tempToken kendi değişkenine atanıyor (ki onu yok sayıyorsun), geriye kalan her şey de safe içine düşüyor. Orijinal user nesnesine ise hiç dokunulmuyor.
Spread Operatörünün Kopyalamadığı Şeyler
Bilmekte fayda olan birkaç ince detay var:
- Non-enumerable (sayılamayan) property'ler kopyalanmaz. Normalde oluşturduğun property'lerin çoğu varsayılan olarak enumerable'dır, ama
Object.definePropertyile tanımlananlar ve bazı yerleşik property'ler bu kurala uymaz. - Prototype kopyalanmaz.
{ ...instance }yazdığında sana orijinal sınıfın bir örneğini değil, düz bir nesne döner. Sınıfın prototype'ında tanımlı metotlar kopyada bulunmaz. - Getter'lar çalıştırılır. İçinde getter olan bir nesneyi spread ettiğinde, getter bir kez çağrılır ve dönen değer yeni nesnede sıradan bir property olarak saklanır.
copy nesnesinde x ve y var, ama elimizde düz bir obje — distance ise Point.prototype üzerinde yaşıyor ve spread oraya hiç uğramıyor. Bir sınıf örneğini klonlamak istiyorsan, sınıfın kendi clone metodunu sunması gerekir genellikle.
Sırada: Dizi Metotları
Spread, değişmez veri (immutable data) araç kutusunun sadece bir parçası. Diğer büyük parça ise dizi metotları — map, filter, reduce ve arkadaşları — orijinal diziyi değiştirmek yerine yeni diziler üretir. Bir sonraki sayfanın konusu da tam olarak bu.
Sıkça Sorulan Sorular
JavaScript'te ...obj ne işe yarar?
Bir object literal içinde ...obj yazdığınızda, o nesnenin kendi enumerable property'lerinin hepsi yeni nesneye kopyalanır. Yani { ...user }, user ile aynı key-value'lara sahip yepyeni bir nesne üretir. Orijinali bozmadan nesne klonlamanın ya da birleştirmenin en yaygın yolu budur.
JavaScript'te iki nesneyi nasıl birleştiririm?
İkisini de yeni bir object literal içine spread edin: const merged = { ...a, ...b }. Ortak key'lerde sonra gelen kazandığı için b'deki değerler a'yı ezer. Bu aslında Object.assign({}, a, b) ile aynı şeyi yapar, sadece çok daha okunaklıdır.
Object spread deep copy yapar mı?
Hayır. Object spread sadece shallow copy yapar — yani yalnızca en üst seviyedeki property'leri kopyalar, iç içe nesneler ve diziler hâlâ aynı referansı paylaşır. copy.address.city değerini değiştirirseniz original.address.city de değişir. Gerçek bir deep copy için structuredClone(obj) kullanın.
Spread ile rest arasındaki fark nedir?
İkisi de ... olarak yazılır ama tam tersi işler yaparlar. Spread bir nesneyi veya diziyi açar, property/eleman tek tek dağıtılır: { ...user }. Rest ise geri kalanları toplar, tek bir değişkene koyar: const { name, ...others } = user. Hangisi olduğunu kullanıldığı yerden anlarsınız.