Alt Çizgi Yaklaşımının Sorunu
JavaScript uzun yıllar boyunca gerçek anlamda private field desteği sunmadı. Yaygın çözüm, özelliğin başına bir alt çizgi koyup herkesin bu kurala uymasını ummaktı:
_count gizli gibi duruyor ama aslında değil. Dışarıdan herhangi biri onu okuyabilir, yazabilir, hatta silebilir. Alt çizgi sadece kapıya asılmış kibar bir "girmeyin" levhası; kapının kendisi ardına kadar açık.
Modern JavaScript bu soruna gerçek bir çözüm getirdi: # ile işaretlenen private field'lar.
# ile Gerçek Gizlilik
Alanın adının önüne hem tanımda hem de her kullanım yerinde # koymanız yeterli:
Sınıfın içinden this.#count sorunsuz çalışır. Ama dışarıdan baktığında böyle bir şey yoktur:
# aslında alanın isminin bir parçası. Diğer dillerdeki private gibi bir modifier anahtar kelime değil — parser'ın nesne üzerindeki ayrı, korumalı bir slotu bulmak için kullandığı bir sigil. Hatanın kod çalışmadan, daha parse aşamasında ortaya çıkmasının sebebi de bu.
JavaScript'te private method ve getter kullanımı
Private yapılabilen tek şey alanlar değil. Method'lar, getter'lar ve setter'lar da # önekini kabul ediyor:
#assertPositive aslında bir iç yardımcı metot. Public API'nin parçası değil, dolayısıyla gerçekten private yaparak dışarıdan yanlışlıkla çağrılmasını engellemiş olursunuz — kimse ona bel bağlayamaz, siz de ileride rahatça yeniden adlandırabilir ya da kaldırabilirsiniz.
Private Static Üyeler
Static üyeler de private olabilir. Aynı şekilde başlarına # koymanız yeterli:
Private static alanlar instance'larda değil, sınıfın kendisinde yaşar. Sınıfın dışına sızmaması gereken sayaçlar, cache'ler veya yapılandırma değerleri için birebirdir.
Alt sınıflar bu alanları göremez
Java ya da C# dünyasından gelenleri genellikle en çok şaşırtan nokta burası. JavaScript'teki private field'lar instance'a özel değil, sınıfa özeldir. Yani bir alt sınıf, üst sınıfın private alanlarına uzanamaz:
JavaScript'te protected diye bir şey yok. Alt sınıfın bu veriye ihtiyacı varsa, üst sınıfın bunu bir metot, getter ya da (daha az tercih edilen) private olmayan bir alanla dışarı açması gerekir. Bu tasarım bilinçli: private gerçekten private demektir ve kalıtım bu gizliliği delmez.
in ile private field kontrolü
Bazen elinizdeki nesnenin gerçekten sizin sınıfınıza ait olup olmadığını doğrulamak istersiniz — buna brand check deniyor. in operatörü, sınıfın içinde private field isimleriyle birlikte çalışır:
#balance alanını yalnızca Wallet sınıfı oluşturabildiği için, #balance in obj ifadesi obj nesnesinin gerçekten bir Wallet örneği olduğunu doğrulamanın güvenilir bir yoludur. Private alanlar dışarıdan taklit edilemediği için, bazı uç durumlarda bu yöntem instanceof operatöründen hem daha hızlı hem de daha güvenlidir.
Sık Karşılaşılan Tuzak: Düz Nesnelerde Private Alan Yoktur
Private alanlar yalnızca sınıf constructor'ı tarafından oluşturulan örneklerde bulunur. new ile oluşturulmamış bir nesne üzerinde bu alanı kullanmaya kalkarsanız hata alırsınız:
Metodu Point olmayan bir this ile çağırdığınızda runtime'da hata fırlatılır. Yukarıdaki brand check'in arkasındaki mekanizma tam olarak budur — private field'lar, "doğru görünen" herhangi bir nesneye değil, onları tanımlayan sınıfa bağlıdır.
# Ne Zaman Kullanılmalı?
Bir state parçası ya da yardımcı metod sınıfın public API'ının parçası değilse, varsayılan tercihiniz private field olsun. Nedenleri şöyle:
- Refactor özgürlüğü. Çağıran kod, göremediği şeylere bağımlı olamaz.
- Gerçek encapsulation. Dışarıdan kazara okuma, yazma veya silme yok.
- Daha temiz otomatik tamamlama. Editörler private üyeleri dışarıdaki çağıranlara önermez.
Bir şey gerçekten arayüzün parçasıysa public property kullanın. Bir private field'a salt okunur erişim vermek istediğinizde getter (get name()) tercih edin. Alt çizgi (_) konvansiyonunu ise artık unutun — o, dilin şimdi kapattığı bir boşluk için geliştirilmiş geçici bir çözümdü.
#celsius değeri asıl sakladığımız yer; celsius ve fahrenheit ise sadece okuma amaçlı birer pencere. Dışarıdan bu sınıfı kullanan kod iç durumu bozamaz, biz de ileride değeri nasıl sakladığımızı dilediğimiz gibi değiştirebiliriz.
Sırada: Prototipler
Aslında sınıflar (class), JavaScript'in altında yatan prototip sisteminin üzerine serpiştirilmiş bir söz dizimi şekerinden ibaret — dilin gerçekten üzerine inşa edildiği daha eski ve temel model prototiplerdir. Prototipleri anladığınızda this'in neden öyle davrandığı, kalıtımın perde arkasında nasıl işlediği ve bir sınıfın extends ile ne yaptığı kafanızda oturacak. Bir sonraki sayfada bunları ele alıyoruz.
Sıkça Sorulan Sorular
JavaScript'te private alan nasıl tanımlanır?
Alan adının başına # ekliyorsunuz — hem tanımlarken hem de her erişiminizde. Örneğin class Counter { #count = 0; increment() { this.#count++; } } gerçek anlamda private bir alan oluşturur. # bir operatör değil, ismin kendisinin bir parçasıdır.
#field ile _field arasındaki fark nedir?
#field ile _field arasındaki fark nedir?_field sadece bir isimlendirme konvansiyonudur — property hâlâ public'tir ve isteyen okur, isteyen yazar. #field ise dilin kendisi tarafından zorlanır: sınıfın dışındaki kod ona kelimenin tam anlamıyla erişemez, erişmeye çalışırsanız parse aşamasında SyntaxError alırsınız. Gerçek gizlilik istiyorsanız # kullanın.
Alt sınıflar (subclass) üst sınıftaki private alanlara erişebilir mi?
Hayır. Private alanlar yalnızca onları tanımlayan sınıfın kapsamındadır — alt sınıflar bile onlara dokunamaz. Eğer subclass'ın erişmesi gerekiyorsa, parent sınıfın bir metot ya da getter ile bunu açık şekilde sunması gerekir. Bu yaklaşım diğer dillerdeki protected anahtar kelimesinden daha katı bir kısıtlamadır ve bu bilinçli bir tasarım tercihidir.
Bir nesnede private alan olup olmadığını nasıl kontrol ederim?
Sınıfın içinde in operatörünü kullanarak: #field in obj ifadesi, obj nesnesinde o private alan varsa true döner. Bu özellikle brand check için işe yarar — yani bir metodu çağırmadan önce nesnenin gerçekten sizin sınıfınızın bir instance'ı olduğunu doğrulamak için.