JavaScript’te Prototypal Inheritance (Prototip Tabanlı Kalıtım)

JavaScript, nesne tabanlı bir dil olup, nesneler ve kalıtım ile çalışmak için çeşitli yöntemler sunar. Ancak JavaScript’in diğer nesne tabanlı dillerden farklı olarak kullandığı Prototypal Inheritance (prototip tabanlı kalıtım) mekanizması, JavaScript öğrenmeye yeni başlayanlar için bazen kafa karıştırıcı olabilir. Bu yazıda, prototip tabanlı kalıtımın ne olduğunu, nasıl çalıştığını, nasıl kullanılacağını ve diğer kalıtım yöntemlerinden farklarını inceleyeceğiz.

Prototypal Inheritance Nedir?

Prototypal Inheritance, JavaScript’te bir nesnenin diğer nesnelerin özelliklerini ve metotlarını devralmasını sağlayan bir mekanizmadır. Yani, bir nesne başka bir nesnenin prototipine dayanarak özellik ve metotlar alabilir. JavaScript’te her nesne, bir prototip (üst nesne) referansına sahiptir. Eğer bir nesne, kendi içinde aranan bir özelliği veya metodu bulamazsa, bu özellik veya metod prototipe bakılarak aranır.

Prototypal inheritance, klasik sınıf tabanlı kalıtımdan farklıdır çünkü her nesne doğrudan başka bir nesneden türetilir, sınıflar arasındaki ilişkiler ise prototipler aracılığıyla kurulur.

JavaScript'te Prototipler ve proto Özelliği

JavaScript nesnelerinin her biri bir prototipe sahiptir. Bu prototipler, nesnenin oluşturulma şekline bağlı olarak farklılık gösterebilir. Bir nesne oluşturduğunda, onun prototipi o nesnenin "üst nesnesi" olarak kabul edilir.

proto Özelliği

Her JavaScript nesnesi, kendisinin prototipine ulaşmak için __proto__ özelliğine sahiptir. Bu özellik, bir nesnenin hangi prototipe ait olduğunu gösterir. Eğer nesne, prototipinde aranan bir özelliği bulamazsa, bu arama prototipin prototipine kadar devam eder. Bu işlem, JavaScript’in prototip zinciri olarak bilinir.

Prototip Zinciri (Prototype Chain)

Bir nesne, prototipine (üst nesnesine) bağlı olarak özellik ve metodlara erişebilir. Eğer nesnede aradığınız özellik ya da metod bulunmazsa, JavaScript otomatik olarak prototip zincirinde bir üst prototipe bakar. Bu zincir, son prototipe kadar devam eder.

Örneğin, aşağıdaki gibi bir nesne oluşturduğumuzda:

const insan = {
  ad: 'Ahmet',
  yas: 30,
  selamla: function() {
    console.log('Merhaba, benim adım ' + this.ad);
  }
};

const ogrenci = Object.create(insan);
ogrenci.okul = 'Üniversite';

Burada, ogrenci nesnesi insan nesnesini prototip olarak alır. Bu durumda, ogrenci nesnesi insan nesnesinin selamla metodunu ve ad, yas özelliklerini miras alır.

console.log(ogrenci.ad); // Ahmet
ogrenci.selamla(); // Merhaba, benim adım Ahmet

Bu örnekte, ogrenci nesnesi insan nesnesinin özelliklerini ve metodlarını devralmıştır. Ancak, ogrenci nesnesinin kendisinde selamla metodu tanımlanmadığı için, JavaScript bu metodu insan nesnesinde arar.

Prototip Tabanlı Kalıtım ile Nesne Oluşturma

JavaScript’te yeni nesneler oluştururken, prototip tabanlı kalıtım kullanarak daha esnek bir yapı oluşturabilirsiniz. Bu genellikle Object.create() yöntemi ile yapılır. Object.create() metodu, yeni bir nesne oluşturur ve verilen prototipe dayalı olarak bu nesneyi oluşturur.

Object.create() Yöntemi ile Nesne Oluşturma

const person = {
  introduce: function() {
    console.log(`Merhaba, benim adım ${this.name}`);
  }
};

const ali = Object.create(person);
ali.name = 'Ali';
ali.introduce(); // Merhaba, benim adım Ali

Bu örnekte, ali nesnesi person nesnesinin prototipi ile oluşturulmuştur. Sonrasında ali nesnesine yeni bir özellik (name) eklenmiş ve introduce metodunu çağırarak, prototip tabanlı kalıtımı kullanarak işlevsellik eklenmiştir.

Prototypal Inheritance ile Klasik Nesne Yönelimli Programlamaya Benzer Yapılar Kurmak

JavaScript, prototip tabanlı kalıtım sunan bir dil olmasına rağmen, modern JavaScript (ES6 ve sonrası) ile class yapısı da eklenmiştir. Ancak, yine de class yapıları, arka planda prototip tabanlı kalıtım kullanmaktadır.

Prototip ve Constructor Fonksiyonları

JavaScript’te bir nesne fonksiyonu tanımlayarak prototip tabanlı kalıtımı kullanabilirsiniz. Bir constructor fonksiyonu, bir nesne yaratmak için kullanılır ve bu nesne otomatik olarak prototip zincirine bağlanır.

function Animal(ad) {
  this.ad = ad;
}

Animal.prototype.tanistir = function() {
  console.log(`Merhaba, benim adım ${this.ad}`);
};

const kedi = new Animal('Kara');
kedi.tanistir(); // Merhaba, benim adım Kara

Burada, Animal adlı constructor fonksiyonu bir nesne oluşturuyor ve tanistir metodu, Animal fonksiyonunun prototipine ekleniyor. kedi nesnesi bu metodu devralıyor.

ES6 Class ile Prototip Tabanlı Kalıtım

ES6 ile birlikte JavaScript, prototip tabanlı kalıtımı daha klasik nesne yönelimli dillerde olduğu gibi sınıflar ile gerçekleştirmeye imkan tanır. Ancak burada da temelde hala prototipler kullanılır.

class Animal {
  constructor(ad) {
    this.ad = ad;
  }

  tanistir() {
    console.log(`Merhaba, benim adım ${this.ad}`);
  }
}

const kedi = new Animal('Kara');
kedi.tanistir(); // Merhaba, benim adım Kara

Yukarıdaki örnekte, class yapısı kullanılarak Animal nesnesi oluşturulmuş ve tanistir metodu Animal sınıfının prototipine eklenmiştir. Ancak, her iki örnekte de prototip tabanlı kalıtım kullanıldığı gerçeği değişmemektedir.

Prototypal Inheritance’ın Avantajları

  • Daha Esnek: Prototip tabanlı kalıtım, JavaScript’te daha esnek bir yapı sağlar. Çünkü her nesne, başka bir nesne üzerinden miras alabilir, bu da esnek ve dinamik bir yapı kurmanıza olanak tanır.
  • Dinamik Davranışlar: JavaScript prototipleri, çalışma zamanında nesnelere metot eklemeyi mümkün kılar. Bu, özellikle uygulamanın çalışma esnasında dinamik olarak fonksiyonellik eklemek için oldukça kullanışlıdır.
  • Daha Az Hafıza Tüketimi: Prototip tabanlı kalıtım, metotları her nesne için yeniden tanımlamak yerine, sadece bir kez prototip üzerinde tanımlanmasına olanak tanır. Bu da bellek tasarrufu sağlar.

Sonuç

JavaScript’te Prototypal Inheritance, nesne tabanlı programlamanın temel taşlarından biridir. Nesnelerin, diğer nesnelerden özellik ve metot devralmasını sağlayan bu mekanizma, dilin esnekliğini artırır. Prototip tabanlı kalıtım, klasik sınıf tabanlı kalıtım anlayışından farklı olsa da, nesneler arasındaki ilişkiyi kurmanın etkili bir yoludur.

Prototip tabanlı kalıtımı anlamak ve doğru kullanmak, JavaScript’in derinliklerine inmek isteyen her geliştirici için önemli bir adımdır. Modern JavaScript’te kullanılan class yapısı bile, arka planda prototip tabanlı kalıtımı kullanır ve bu mekanizmanın temellerini anlamak, daha etkili ve verimli kod yazmanızı sağlar.