Neden prototype a ihtiyaç duyarız ?
Aşağıdaki en temel obje oluşturma örneğine bakalım
Objeyi oluşturduk. Şimdide uygulamanızda birden fazla insana ihtiyacınızın olduğu bir senaryo düşünün.Bu durumda sizin için ‘insan’ objesini oluşturacak ayrı bir fonksiyon oluştururduk.
Şimdi daha karmaşık bir uygulamada milyonlarca insan objesine ihtiyaç duyduğunuz bir senaryoyu düşünün.Böyle bir durumda İnsan objesini her oluşturduğumuzda hafızada yemekYe ve sporYap metodları yeniden oluşturulacaktır. Bunun hafızada kaplayacağı yeri tahmin edebilmişsinizdir.
Yukardaki gibi her obje için bu metodları yeniden oluşturmak yerine, bir kere oluşturup bunları İnsan fonksiyonunda kullanırsak, böyle bir durumda bu metodları hafızada sadece bir kere oluşturmuş oluruz.
Yukardaki örnekte insanMetodları objesini hafızada bir kere oluşturup , yeni bir insan oluşturduğumuzda bu objenin metodlarını referans olarak aldık. Bu şekilde varolan bir problemimizi çözmüş olduk. Değil mi ?
Aslında hayır.
Kurduğumuz bu yapıya yeni bir metod eklemeye karar verdiğimizi düşünelim. Bu durumda insanMetodları objesine yeni bir metod eklememiz ve İnsan fonksiyonunu eklediğimiz yeni metoda referans gösterecek şekilde düzenlememiz gerekir.
Hem yukardaki gibi eklemeler yapabilip hemde her şeyi değiştirmek zorunda kalamasak iyi olmaz mıydı ? İşte prototype ın ve javascriptin hünerlerini sergilediği yer burası.
PROTOTYPE
Prototype dediğimiz şey aslında bir JavaScript fonksiyonunun property sidir. Her fonksiyon oluşturduğumuzda JavaScript motoru buna ekstra olarak bir property ekler. Eklenen property prototype dır.Bu protoype property sinin kendiside bir objedir. Ve bunun kendisine ait bir constructor adında property si vardır. Bu constructor property si oluşturduğumuz objenin kurucu fonksiyonunu işaret eder. Aşağıda ki resimde bunu görselleştirmeye çalıştım.
prototype property si fonksiyonun Prototype objesini işaret eder. Fonksiyonun prototype objesine fonksiyonAdı.prototype şeklinde ulaşabiliriz. Prototype objesindende fonksiyonun kendisine fonksiyonAdı.prototype.constructor şeklinde ulaşabiliriz. Şimdi bunu biraz kod yazarak anlatalım.
Konsola çıktısı şöyle :
Yukarda gördüğünüz gibi İnsan.prototype objesini yazdırdığımızda, objenin constructor, __proto__ şeklinde 2 property sini gösterdi(Şimdilik __proto__ property sini bir kenara atalım).Şimdi “Neymiş bu constructor propertysi” diyip İnsan.prototype.constructor ı yazdıralım.
Gördüğünüz gibi constructor property si tanımladığımız fonksiyonun kendisini tutuyor(Prototype başlığının altındaki ilk diyagrama bakınız).Söylediğim gibi bir fonksiyon oluşturduğumuzda bu JavaScript motoru bu fonksiyona prototype diye bir property ekliyor. Bu property prototype diye bir obje tutuyor. Bu prototype objesininde constructor diye bir property si var. Ve bu property de oluşturduğumuz fonksiyonun kendisini tutuyor. Şimdi bu bahsettiğimiz şeylerin kontrolünü yapalım.
Yukarda bahsedilenlere göre, İnsanın prototype objesinin constructor property sinin tutuğu şey aslında İnsan fonksiyonun kendisi mi diye bir soru yönelttik bilgisayara ve true cevabını aldık. Aynı şekilde İnsanın prototype objesi aslında İnsanın prototype objesinin constructor property sinin tuttuğu objenin prototype objesi midir diye bir soru yönelttik ve true cevabını aldık.
Şimdi bu anlatılan şeyleri birleştirelim.
En başta bahsettiğimiz problemi prototype kavramıyla çözelim. Oluşturulan her insan objesine metod eklemek yerine bu eklediğimiz metodları insanı oluşturan kurucu fonksiyonun prototype objesine ekleyelim ve Bu kurucu fonksiyondan Erkan adında bir obje oluşturalım. Ve erkanı ekrana yazdıralım
Çıktı:
Farkettiğiniz gibi oluşturduğumuz metodlar Erkan objesinin property listesinde kayıtlı değil. Bunun sebebi eklediğimiz fonksiyonları objenin kendisine değil objenin kurucu fonksiyonunun prototype objesine ekledik. Burda Erkan objesinde bizim eklemediğimiz proto isimli property daha var. Basitçe anlatmak gerekirse bu proto aslında kurucu fonksiyonumuzun getter ve setter ıdır diyebiliriz.
Ne yaptığımıza bakalım
- İnsan adında bir kurucu fonksiyon tanımladık.
- Metodlarımızı bu kurucu fonksiyonun prototype objesine ekledik.
Burda bir virgül koyalım. Bir fonksiyonun prototype objesine eklediğimiz şeyler o fonksiyon ile türeyen bütün objelerde kullanılabilir hale gelir. Yaptığımız şeylere incelemeye devam edelim.
Şimdi İnsan kurucu fonksiyonuyla obje oluşturduğumuzda ne olduğuna bakalım
- Objeyi İnsan fonksiyonun yanında new kelimesini kullanarak oluşturduk.
- Bundan sonra JavaScript motoru arkaplanda objeye o objeyi oluşturan fonksiyonun prototype objesinin getter ve setter ı olan proto property sini ekler. Yukarda yazdığım kodu basit bir görselle anlamaya çalışalım
Eğer Erkan objesinin __proto__ propertysi İnsan kurucu fonksiyonun işaret ediyorsa ozaman bu property yemekYe, uyu, yürü metodlarınıda gösteriyor olmalı.
Burdan şu sonucu çıkarabiliriz eğer console.log(erkan.proto === İnsan.prototype) kodunu çalıştırırsak çıktı olarak true alırız.
Şimdi mehmet adında İnsan kurucu fonksiyonu ile yeni bir obje oluşturalım ve bu objenin proto property sinin neyi işaret ettiğine bakalım.
Burda gördüğümüze sonuca göre Mehmetin __proto__ property side yine İnsan kurucu fonksiyonunun prototype objesini işaret ediyor. O zaman Mehmetin __proto__ property sini ,Erkanın __proto__ property ile karşılaştırıp bunun doğru olduğunu görebiliriz.
Yaptığımız çıkarımın doğru olduğunu görmüş olduk. yine olayı görselleştirelim.
Son anlattıklarımıza göre artık bir kurucu fonksiyonun prototype objesine eklediğimiz şeylerin o fonksiyondan türeyen objelerin herbirine eklenip hafızada yer kaplaması yerine , fonksiyondan türeyen her obje için kurucu fonksiyonun prototype objesi üzerinden erişilebilir olduğuna emin olabiliriz.
Prototype Chain (Prototype Zinciri)
Şu ana kadar Prototype ın ne olduğunu dair sağlam bir temel attık. Şimdi Prototype zincirinin ne olduğunu inceleyelim. Bunu anlayabilmek için ufak bir egzersiz yapalım. Boş bir obje oluşturalım ve bu objeyi alert fonksiyonunu kullanarak gösterelim.(Bu noktadan sonra bazı kod örneklerini hazırlarken Google Chrome debug console kullandım).
Oluşturduğumuz boş objede kayıtlı hiçbir veri yok ama bu object Object nedir ? Ne anlatmaya çalışıyor bize ? Biraz daha derinlere inelim. elma yı yazdıralım.
Objenin içi boş ama objeyi açarsak proto propertysini içerdiğini görürüz. Bunun bir fonksiyonun prototype objesini işaret ettiğini öğrendik. Bunuda yazdırıp ne olduğunu görebiliriz
Büyük bir karmaşayla karşılaştık. Bir sürü fonksiyon tanımlı. Bu fonksiyonlar nerden geliyor ? Bu prototype objesi hangi kurucu fonksiyona ait ? Bunların cevabına bakmadan önce alert olarak gelen object Object çıktısını nasıl elde ettiğimizin cevabını öğrenelim. Prototype konusundan öğrendimiz şeylere göre bir obje __proto__ sunda tuttuğu prototype objesinin metodlarını kullanabiliyordu. Bu prototype kayıtlı toString: property sinin tuttuğu toString() metodunu çalıştırmayı deneyelim.
Evet aradığımız şeyi bulduk. alert(elma) kodunu çalıştırdığımızda JavaScript motoru kime ait olduğunu bilmediğimiz bu prototype objesinde kayıtlıı toString() metodunu çalıştırdı. Peki bu prototype objesi kime ait ?
Diğer dillerdede olduğu gibi JavaScript tede önden tanımlı objeler vardır. Object,Array,Date,Function(JavaScriptte fonksiyonlarda obje kabul edilir).Ve bu önden tanımlı objelerin hepsinin kendine ait bir prototype objesi mevcuttur. Yukarda elma objesini oluştururken Object kurucu fonksiyonunu çağırdık. Çünkü const elma = {} , JavaScript motoru için. const elma = new Object() anlamına geliyor. Burdaki Object önden tanımlı dediğimiz kurucu fonksiyonlardan biri ve bu korucu fonksiyonun Object.prototype ile ulaşabileceğimiz bir prototype objesi bulunuyor. Anlattıklarımızı kontrol etmek için boş elma objesinin __proto__ sunu önden tanımlı Object kurucu fonksiyonunun prototype objesiyle karşılaştıralım.
Bunu görselleştirirsek:
Şimdi yavaş yavaş prototype chainden bahsedebiliriz. Önden tanımlı Object in prototype objesinin __proto__ propertysine ulaşmaya çalışırsak null çıktısını alırız. Bunun sebebini anlamak için JavaScriptin olaya yaklaşım biçime bakalım. JavaScriptte bir objenin propertysine ulaşmaya çalıştığımızda ( erkan.ad, erkan.yas, erkan.yemekYe(), erkan.yürü() vs) JavaScript motoru önce objenin kendisinde bu property nin var olup olmadığına bakar. Eğer varsa kullanır yoksa bu objeyi oluşturan kurucu fonksiyonun prototype objesinin bu propertyi içerip içermediğine bakar(İnsan.prototype). İçeriyorsa bunu alıp kullanır. İçermiyorsa o kurucu fonksiyonun varsa __proto__ propertysinin tuttuğu prototype objesini kontrol eder. Varsa kullanır. Yoksa ve biz bu kurucu fonksiyonun __proto__ suna müdahale etmediysek, JavaScript motoru tarafından önden tanımlı Object sınıfının prototype objesini gösterecek şekilde düzenlenir. Günün sonunda bizim tanıdığımız bütün objeler ve bizim tanımdalığımız kurucu metodlardan oluşturduğumuz objeler önden tanımlı Object kurucu fonksiyonunun prototype objesine bağlanır. Önden tanımlı object kurucu fonksiyonun __proto__ propertyside null u işaret eder. Günün sonunda en tepede null vardır. Buna Prototype Chain(Prototype Zinciri) denir. Bizim tanımladığımız objelerde var olmayan property lere ulaşmaya çalışırken undefined cevabını alamamızın sebebi budur. Çünkü JavaScript motoru null değerine ulaşana kadar __proto__ zincirini kontrol eder ve en sonunda elde ettiği şey null olunca undefined döndürür.
Kullandığımız İnsan kurucu fonksiyonu örneğine dönelim. Oluşturduğumuz bu fonksiyonun __proto__ property sine müdahale etmedik. Böyle bir durumda JavaScript motoru tarafından İnsan kurucu fonksiyonunun __proto__ propertysi önden tanımlı Object kurucu metodunu gösterecek şekilde oluşturulur.
Gördüğünüz gibi bu kurucu İnsan prototype nın __proto__ property si müdahale etmediğimiz için Object kurucu metodunun prototype ını işaret ediyor. Object prototype ının __proto__ sunu kontrol edelim yani ahmet.__proto__.__proto__.__proto__ kodunu çalıştıralım.(ki bu Object.__proto__ demektir).
Anlattığımız şeylerin sağlamasını yapmış olduk. Ek olarak vermem gereken bir bilgi var. Bahsettiğimiz prototype zincirinde bir property ararken objeye en yakın olan property tercih edilir.Yine örnek verelim. İnsan kurucu fonksiyonunun proto suna müdahale ettiğimizi düşünelim ve farklı bir kurucu fonksiyonu göstermesini sağlayalım. Bu kurucu fonksiyonun yine yemekYe adında bir metodunun olduğunu düşünelim. Bu durumda JavaScript motorunun takip edeceği zincir erkan → İnsan → YeniPrototype → Object → null olur. Ama yolculuğu bu YeniPrototype a varmadan sonlanır. Çünkü İnsan prototype ı hali hazırda yemekYe() metodunu içerir ve bu noktadan sonra JavaScript motorunun aramaya devam etmesine gerek kalmamıştır.
Son bir paragrafla her şeyi toparlayıp yazımı sonlandırayım.
JavaScriptte kalıtım olayı diğer dillere göre biraz daha farklı işler. Bir sınıfta deftere bir şeyler yazması gereken Ahmet, Ali , Çetin adında 3 öğrencinin olduğunu düşünelim. Yazı yazmak için sadece bir kaleme ihtiyaç duyarız.Ama bu 3 öğrenci arasından sadece Ahmet’in kalemi vardır.Şimdi bu problemi çözmek için 2 seçenek vardır. İlk seçenekte Ali ve Çetin Ahmetin elindeki kalemi inceler ve kantine inip 2 adet kalem alırlar. Kalıtım olayına diğer dillerim yaklaşımı bu şekildedir. Yani her bir Öğrenci için 1 adet kalem. Her bir kaleme 5 tl gibi bir fiyat verirsek. Toplamda 15 tl harcamış oluruz. Bu problemi çözmek için kullanabileceğimiz diğer yöntemde Ali ve Çetin kantine inip yeni kalem almak yerine bir kontrol zincirini takip ederler. Çetin kalem ihtiyacını karşılamak için Ahmet’in zaten varolan kalemini ödünç alır ve kullandıktan sonra Ahmet’e kalemi geri verir. Ali’den çetinin yaptığı şeyi yapar. Bu senaryoda tanesi 5 lira kalem için harcadığımız toplam tutar 5tl dir. Çetin önce Ali’ye kalemi olup olmadığını sorar eğer yoksa, sonra Ahmet’e kaleminin olup olmadığını sorar, Ahmet’ede kalem yoksa kantine sorar ve kantindede yoksa kalem bulamadım değerini döndürür(yani null değerini). Bu noktada Çetinin takip ettiği zincir Ali→ Ahmet → Kantin→ null dur. JavaScript in kalıtıma bakış açısı 2. senaryodur.
Bugünki yazımın sonuna gelmiş bulunmaktayız sormak istediğiniz bir şey veya derinine inmemi istediğiniz bir konu olursa bana
[email protected] üzerinden ulaşabilirsiniz. Hepinize iyi çalışmalar diliyorum.
Kalemine sağlık erkan güzel bir yazı olmuş.