Prototypal Inheritance & Chaining
Herkese Merhaba :)
Bugünkü yazım Advanced Javascript konularından biri olan ve yine mülakatalarda karşımıza çıkabilen bir konu üzerine olacak : Prototypal Inheritance & Chaining
Bunun için ise öncelikle Constructor Fonksiyonların ne olduğunu öğrenmemiz gerek. O halde başlayalım 🤗
Constructor Fonksiyonlar
En basit açıklaması ile Constructor Fonksiyonların görevi bir obje oluşturmaktır. Bir Constructor Fonksiyon oluşturmak için regular fonksiyon yapısını ya da function expression yapısını kullanabiliriz (değişkene atayarak). PascalCase ile isimlendirilirler.
Şimdi bir örnek üzerinden gidelim. Person isminde bir constructor fonksiyon oluşturacağız — Javascript’te her fonksiyonun bir obje olduğunu unutmayalım — . İçinde bazı özellikler (property) ve bir tane de metod olsun. Ve biz bu Person objemizle aynı özellikleri ve metodları kullanan yeni objeler yaratmak istiyoruz. Bunun için de Person fonksiyonunu çağırmamız gerecek. Ancak her ne kadar regular bir fonksiyon gibi görünse de çağrım şekli normal bir fonksiyondan farklıdır. Person( ) olarak değil de new operatörü kullanılarak çağrılır. Örneğimize bakalım;
let Person = function(firstName, gender, birthday) {
this.firstName = firstName; // property
this.gender = gender; // property
this.birthday = birthday; // property
this.calculateAge = function() { // method
let age = new Date().getFullYear() - this.birthday;
console.log(age);
}
}
let joey = new Person('Joey', 'Male', 1967);
joey.calculateAge();
console.log(joey);
// 56
// Person {firstName: 'Joey', gender: 'Male', birthday: 1967, calculateAge: ƒ}
let phoebe = new Person('Phoebe', 'Female', 1963);
phoebe.calculateAge();
console.log(phoebe);
// 60
// Person {firstName: 'Phoebe', gender: 'Female', birthday: 1963, calculateAge: ƒ}
let chandler = new Person('Chandler', 'Male', 1969);
chandler.calculateAge();
console.log(chandler);
// 54
// Person {firstName: 'Chandler', gender: 'Male', birthday: 1969, calculateAge: ƒ}
Peki bu new operatörünün görevi nedir ? Bir constructor fonksiyon çağrıldığında ne olur ?
- New operatörü boş bir obje oluşturur. — { }
- This değerinin yeni oluşturulan bu boş nesneyi işaret etmesini sağlar.
- Ve son olarak da fonksiyondan bu nesneyi döndürür.
Bu noktada this ile ilgili bir kafa karışıklığı oluştu ise bir önceki yazıma ve özellikle de new binding kısmına bakabilirsiniz.
Şimdi örneğimize tekrar bakalım. Burada Person bizim temel objemizdir. Yani blueprint ya da template obje olarak adlandırılır. Bunun üzerinden oluşturduğumuz yeni objeler de (joey, phoebe, chandler) instance olarak adlandırılır. Instance obje oluşturmaktaki amacımız benzer özellik ve metodlara sahip objeler yaratmaktır.
Herşey güzel görünüyor. Tüm özellikler ve metodlarla, işlem yapabiliyoruz. Ancak burada şöyle bir sorun meydana gelmektedir. Biz joey, phoebe ve chandler nesnelerini oluştururken bu nesnelerin hepsine firstName, gender, birthday ve calculateAge metodu eklenir. Farz edelim ki eğer Person objesini kullanarak binlerce nesne yaratırsak, bellekte calculateAge metodunun 1000 tane kopyası olacaktır. Bu da DRY ilkelerini ihlal ettiğimiz ve bellek depolama alanını boşa harcadığımız anlamına gelir. İşte burada inheritance devreye girer. Buradaki çözüm, calculateAge metodunun tek bir kopyasını oluşturmak ve tüm nesnelerin bu metoda erişimini sağlamaktır.
Inheritance Nedir ?
Inheritance bir objenin başka bir objedeki özellik (property) ve metodlara erişim sağlaması demektir. Ve biz bunu Javascript’te Prototype kullanarak gerçekleştirebiliriz.
Peki Prototype Nedir ?
Javascript’te her objenin bir prototype özelliği ( property ) vardır. Bu özellik sayesinde bir obje başka bir objenin metod ve özelliklerine erişim sağlayabilir.
Biz calculateAge metodunu erişilebilir yapmak istiyoruz. O halde şöyle bir düzenlemeye gideceğiz.
let Person = function(firstName, gender, birthday) {
this.firstName = firstName;
this.gender = gender;
this.birthday = birthday;
}
Person.prototype.calculateAge = function () {
let age = new Date().getFullYear() - this.birthday;
console.log(age);
};
let joey = new Person('Joey', 'Male', 1967);
joey.calculateAge();
console.log(joey);
// 56
// Person {firstName: 'Joey', gender: 'Male', birthday: 1967}
İşte bu kadar basit :) Şimdi calculateAge metodu her defasında kopyalanmayacak ama erişilebilir olacak. Biz ihtiyacımız olduğunda kullanabileceğiz.
Şimdi her şey yolunda ise gelelim son kısma : Prototype Chaining
Array ve objelerde kullandığımız metodların nereden geldiğini hiç merak ettiniz mi ? Mesala pop, shift, unshift, push, hasOwnProperty(), valueOf(), toString() gibi.
Biz literal notation kullanarak — { } [ ] ile — bir obje ya da bir array oluşturduğumuzda, oluşturduğumuz array ve objeler default olarak arka planda Javascript tarafından Object Constructor — new Object() — ve Array Constructor — new Array() — üzerinden oluşturulmaktadır.
// let myArr = new Array() -> Javascript arka planda bu şekilde işliyor.
let myArr = [1, 2, 3]
// let movie = new Object() -> Javascript arka planda bu şekilde işliyor.
let movie = {
name: 'Fight Club',
genre: 'Drama, Thriller, Comedy',
releaseDate: '1999',
};
console.log(movie instanceof Object); // true
Böylece bu movie nesnesi, Object Constructor’ın prototype özelliğine erişebilecektir. Ve hasOwnProperty(), valueOf(), toString() vb. metodlarına erişimi olacaktır. joey, phoebe, chandler nesneleri de, Person nesnesinden oluşturuldular. Person nesnesi de, Object Constructor’dan gelmektedir. O halde joey, phoebe ve chandler objeleri hem Person nesnesine hem de Object Constructor prototipine ve en nihayetinde sahip oldukları tüm özellik ve metodlara erişebilirler. İşte Buna prototype chaining denir.
// PROTOTYPE CHAIN
new Object() / Object() -> Person -> joey, phoebe, chandler
Ve Bonus 🤩
En bilindik Constructor Fonksiyon → new Date( )
Bahsedeceklerim bu kadardı. Okuduğunuz için teşekkür ederim. Umarım sizin için faydalı bir yazı olmuştur 🤗