Programlarken genelde bir Åeyi alır ve bunu geniÅletmek isteriz.
ÃrneÄin, kullanici adında bir obje ve bunun özellikleri ve metodları olsun, bunu biraz düzenleyerek admin ve misafir gibi iki farklı obje oluÅturmak isteriz. Yani kullanici objesini doÄrudan kopyalamak veya metodlarını tekrardan uygulamak deÄil bunlar üzerinden yeni objeler yaratmak isteyebiliriz.
Prototip kalıtımı buna olanak saÄlamaktadır.
[[Prototype]]
Javascript objeleri gizli bir özellik olan [[Prototype]] özelliÄine sahiptirler. Bu null olabilir veya baÅka objeye referans verebilir. Referans verilen obje âprototipâ olarak adlandırılır.
[[Prototip]]'in âbüyülüâ bir anlamı bulunmaktadır. Objeden bir özellik okunmak istendiÄinde, ve bu obje bulunamadıÄında JavaScript bunu otomatik olarak prototipâten alır. Programlamada buna prototip kalıtımı denir. Birçok dil özelliÄi ve programlama tekniÄi bunun üzerine kuruludur.
[[Prototpe]] gizli bir özelliktir, fakat bunu ayarlamanın birçok yolu vardır.
Bunlardan biri __proto__ kullanmaktır:
let animal = {
eats: true
};
let rabbit = {
jumps: true
};
rabbit.__proto__ = animal;
Aklınızda bulunsun __proto__ [[Prototype]] ile aynı deÄildir. Bunun için alıcı/ayarlayıcı ( getter/setter)'dır. Bunun hakkında ilerleyen bölümlerde daha fazla açıklama yapılacaktır fakat Åimdilik __proto__ yeterlidir.
ÃrneÄin rabbit adında bir özelliÄe arasanız ve bu özellik yoksa, JavaScript bunu otomatik olarak animalâdan alır.
ÃrneÄin:
let animal = {
eats: true
};
let rabbit = {
jumps: true
};
rabbit.__proto__ = animal; // (*)
// Artık her ikisini de rabbit'te bulabilirsiniz.
alert( rabbit.eats ); // true (**)
alert( rabbit.jumps ); // true
(*) satırında animalâın rabbit in özleliÄi olması saÄlanır
Sonrasında alert rabbit.eats (**)'i okur. Bu rabbitâte olmadıÄından JavaScript [[Prototype]]'ı takip eder ve bunu animalâin içerinde bulur.
Böylece âanimalâ rabbitâin prototipâi veya ârabbit prototipsel olarak animal kalıtımını almıÅtırâ diyebiliriz.
Diyelim ki animalâın birçok özelliÄi ve metodu olsun, bunları otomatik olarak rabbit de kullanabilir. Bu çeÅit özelliklere kalıtılmıŠözellikler denir.
EÄer animalâda bir metodumuz varsa bu metod rabbit tarafından çaÄırılabilir olmaktadır.
let animal = {
eats: true,
walk() {
alert("Animal walk");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
// walk prototipten alınmıÅtır.
rabbit.walk(); // Animal walk
Metod prototipten otomatik olarak Åu Åekilde alınmıÅtır:
Prototip zinciri daha da uzun olabilir:
let animal = {
eats: true,
walk() {
alert("Animal walk");
}
};
let rabbit = {
jumps: true,
__proto__: animal
};
let longEar = {
earLength: 10,
__proto__: rabbit
}
// walk prorotip zincirinden alınmıÅtır.
longEar.walk(); // Animal walk
alert(longEar.jumps); // true (rabbit'ten gelmekte)
Aslında iki tane kısıtlama bulunmaktadır:
- Referanslar kapalı devre olamaz. Böyle bir duurmda hata verir.
__proto__'nun deÄeri ya obje olur ya danullDiÄer türlüsü ( tüm ilkel veri tipleri ) görmezden gelinir.
Ãok açık olsa da tekrar söylemekte yarar var. Bir obje sadece bir tane [[Prototype]]'a sahip olabilir. Bir objenin iki farklı objeden kalıtım alamaz.
Kuralların Okuması/Yazılması.
Prototip sadece özelliklerin okunması için kullanılır.
Veri özelliklerinin yazılma/silinme ( alıcı/ayarlayıcı deÄil) iÅi doÄrudan obje üzerinden yapılır.
AÅaÄıdaki örnekte rabbitâe kendi walk metodu atanmıÅtır:
let animal = {
eats: true,
walk() {
/* Bu metod rabbit tarafından kullanılmayacaktır. */
}
};
let rabbit = {
__proto__: animal
}
rabbit.walk = function() {
alert("Rabbit! Bounce-bounce!");
};
rabbit.walk(); // Rabbit! Bounce-bounce!
Artık rabbit.walk() metodu doÄrudan kendi içerisinde bulur ve çalıÅtırır. Prototip kullanmaz:
Alıcı/Ayarlayıcı için ise eÄer özellik okunursa bu doÄrudan prototipte okunur ve uyarılır.
ÃrneÄin aÅaÄıdaki admin.fullName özelliÄine bakın:
let user = {
name: "John",
surname: "Smith",
set fullName(value) {
[this.name, this.surname] = value.split(" ");
},
get fullName() {
return `${this.name} ${this.surname}`;
}
};
let admin = {
__proto__: user,
isAdmin: true
};
alert(admin.fullName); // John Smith (*)
// Ayarlayıcılar uyarıldı!
admin.fullName = "Alice Cooper"; // (**)
(*) satırında admin.fullName özelliÄi user prototipinde alıcıya sahiptir. Bundan dolayı çaÄırılır. (**) satırında ise ayarlayıcıya sahip olduÄundan bu da çaÄırılır.
âthisâ'in deÄeri
Yukarıdaki örnekte aklınıza Åöyle bir soru gelebilir. set fullName(value) içerisinde thisâin deÄeri nedir? this.name ve this.surname yazılan yerlerde admin mi yoksa user mı kullanılır?
Cevap basittir: this prototip tarafından hiçbir Åekilde etkilenmez.
Metodun bulunduÄu yerin önemi olmaksızın, metod çaÄrısında this her zaman noktadan önceki bölümdür.
Ãyleyese aslında ayarlayıcı adminâi this olarak kullanır. userâı deÄil.
Ãok büyük bir objeye ve buna ait birçok metoda, kalıtıma sahip olabileceÄimizden dolayı bu aslında çok önemli bir olaydır. Sonrasında büyük objenin deÄil kalıtılmıŠobjelerin metodlarını çalıÅtırabilir ve bunların özelliklerini deÄiÅtirebiliriz.
ÃrneÄin burada animal aslında âmetod deposuâ'nu temsil etmektedir. rabbit ise bunu kullanır.
rabbit.sleep() çaÄrısı rabbit üzerinde this.isSleepingâi ayarlar:
// animal metodları
let animal = {
walk() {
if (!this.isSleeping) {
alert(`I walk`);
}
},
sleep() {
this.isSleeping = true;
}
};
let rabbit = {
name: "White Rabbit",
__proto__: animal
};
// rabbit.isSleeping'i modifiye eder.
rabbit.sleep();
alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (prototipte böyle bir özellik bulunmamaktadır.)
Sonuç görseli:
EÄer bird, sname gibi animalâdan miras alan objelere sahip olsaydık bunlar da animalâin metodlarına eriÅebilirlerdi. Fakat her metoddaki this baÄlı bulunduÄu objeye göre çalıÅırdı. Yani noktadan önceki metoda göre, animalâe göre deÄil. Bundan dolayı ne zaman thisâe veri yazılsa o objelerin içerisine yazılır.
Sonuç olarak metodlar paylaÅılsa bile objelerin durumları paylaÅılmaz.
Ãzet
- JavaScriptâte tüm objelerin gizli
[[Prototype]]'ı bulunmaktaıd. Bu özellik ya baÅka bir objedir veyanullâdur. - EriÅmek için
obj.__proto__kullanılabilir. (elbette diÄer yollar da mevcuttur, ilerde bunlara deÄineceÄiz.) [[Prototype]]tarafından temsil edilen objeye âprototipâ denir.- EÄer bir
objânin özelliÄini okumak veya bir metodunu çaÄırmak istersek ve o metod yok ise JavaScript bunu prototipte bulmaya çalıÅır. Yazma/Silme operasyonları doÄrudan obje üzerinde çalıÅtırılır. Ãzellik ayarlayıcı olmadıÄı sürece prototip kullanılmaz. - EÄer
obj.method()'u çaÄırırsak vemethodprototipten alınırsathisyine deobjâi temsil eder. Bundan dolayı metodlar her zaman o anki obje ile çalıÅırlar miras kalsalar bile.
Yorumlar
<code>kullanınız, birkaç satır eklemek için ise<pre>kullanın. EÄer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)