JavaScript'te her nesnenin prototip adı verilen başka bir nesneye gizli bir bağlantısı vardır. Bir özelliği okumak istediğinizde motor önce nesnenin kendisine, bulamazsa prototipine, sonra prototipin prototipine bakar ve null değerine ulaşana kadar devam eder. Bu arama yoluna prototip zinciri denir ve JavaScript'teki kalıtımın tamamı bu mekanizmaya dayanır.
Sınıflar (class) bu modelin üzerine kurulmuş bir sözdizimidir, yeni bir kalıtım sistemi getirmez. Bu yazıda zincirin nasıl çalıştığını, Object.create() ve constructor fonksiyonlarını ve bunların sınıflarla ilişkisini çalıştırılmış örneklerle gösteriyoruz. Sınıf sözdiziminin kendisi için class yazısına bakabilirsiniz.
Özellik araması zincirde nasıl ilerler?

Spesifikasyon nesnenin prototipini [[Prototype]] adlı iç bir yuva olarak tanımlar. Bu yuvayı Object.getPrototypeOf() ile okur, Object.setPrototypeOf() ile değiştirirsiniz. Nesne değişmezi içinde __proto__: x anahtarı da prototipi belirlemenin standart bir yoludur (eski obj.__proto__ erişimcisinden farklıdır).
const arac = { tekerlek: 4, tur: "araç" };
const motosiklet = { tekerlek: 2, __proto__: arac };
console.log(motosiklet.tekerlek); // 2 (kendi özelliği, prototiptekini gölgeler)
console.log(motosiklet.tur); // araç (prototipten geldi)
console.log(motosiklet.renk); // undefined (zincirin sonuna kadar arandı)
console.log(Object.getPrototypeOf(motosiklet) === arac); // true
console.log(Object.getPrototypeOf(arac) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // nullBuradaki zincir şöyledir: motosiklet, arac, Object.prototype, null. Nesnenin kendisinde de prototipinde de aynı isimli özellik varsa kendisininki kazanır; buna özellik gölgeleme (property shadowing) denir. Bir nesneye değer atadığınızda ise (setter devreye girmiyorsa) her zaman nesnenin kendi özelliği oluşur, prototip değişmez.
Miras alınan metotlarda this
Prototipten gelen bir fonksiyon çağrıldığında this, fonksiyonun tanımlandığı prototipi değil, çağrının yapıldığı nesneyi gösterir. Kalıtımı kullanışlı kılan da budur: tek bir metot, her nesnenin kendi verisiyle çalışır.
const ebeveyn = {
deger: 2,
hesapla() {
return this.deger + 1;
},
};
const cocuk = { __proto__: ebeveyn };
console.log(cocuk.hesapla()); // 3 (deger, ebeveynden okundu)
cocuk.deger = 4; // cocuk üzerinde kendi özelliği oluşur
console.log(cocuk.hesapla()); // 5
console.log(ebeveyn.deger); // 2Constructor fonksiyonları ve prototype özelliği
Burada iki farklı şeyi karıştırmamak önemli. Object.getPrototypeOf(obj) bir nesnenin prototipidir. Fonksiyon.prototype ise bir fonksiyonun sıradan bir özelliğidir ve o fonksiyon new ile çağrıldığında oluşan örneklerin prototipi olarak kullanılır. Varsayılan olarak bu nesnede, fonksiyonun kendisini gösteren bir constructor özelliği bulunur.
function Kutu(deger) {
this.deger = deger;
}
Kutu.prototype.getir = function () {
return this.deger;
};
const k1 = new Kutu(1);
const k2 = new Kutu(2);
console.log(k1.getir === k2.getir); // true (tek kopya, paylaşılıyor)
console.log(Object.getPrototypeOf(k1) === Kutu.prototype); // true
console.log(Kutu.prototype.constructor === Kutu); // true
// Prototip sonradan değişirse mevcut örnekler de etkilenir
Kutu.prototype.getir = function () {
return this.deger * 10;
};
console.log(k1.getir()); // 10Arrow function'ların varsayılan bir prototype özelliği yoktur; bu yüzden constructor olarak kullanılamazlar:
console.log(typeof function () {}.prototype); // object
console.log((() => {}).prototype); // undefinedObject.create() ile prototipi doğrudan belirlemek
Object.create(proto), prototipi proto olan yeni ve boş bir nesne döndürür. İkinci parametre olarak özellik tanımlayıcıları (property descriptor) verebilirsiniz; bu sayede nesne değişmeziyle yapılamayan şeyler, örneğin numaralandırılamayan veya yazılamayan özellikler, oluşturma anında tanımlanabilir. proto ne null ne de bir nesneyse TypeError fırlatılır.
const kisiProto = {
tanit() {
return `Ben ${this.ad}`;
},
};
const ali = Object.create(kisiProto);
ali.ad = "Ali";
console.log(ali.tanit()); // Ben Ali
// Prototipsiz nesne: Object.prototype metotlarını miras almaz
const sozluk = Object.create(null);
sozluk.elma = "apple";
console.log(sozluk.hasOwnProperty); // undefined
console.log(Object.hasOwn(sozluk, "elma")); // true
// Tanımlayıcı ile: varsayılan olarak yazılamaz ve numaralandırılamaz
const sabit = Object.create({}, { kod: { value: 42 } });
console.log(sabit.kod, Object.keys(sabit)); // 42 []
try {
Object.create(5);
} catch (e) {
console.log(e.constructor.name); // TypeError
}Object.create(null) ile oluşan nesne, anahtarları kullanıcıdan gelen sözlükler için kullanışlıdır çünkü toString gibi miras alınmış isimlerle çakışma olmaz. Bu tür nesnelerde obj.hasOwnProperty() bulunmadığı için Object.hasOwn() kullanın. Anahtar-değer saklamak için başka bir seçenek de Map ve Set yapılarıdır.
Sınıflar ve prototip zinciri aynı şeydir

class Derived extends Base yazdığınızda arka planda şu zincir kurulur: örnek, Derived.prototype, Base.prototype, Object.prototype, null. Sınıf gövdesindeki metotlar Derived.prototype üzerinde durur. Bunu doğrudan kontrol edebilirsiniz:
class Base {}
class Derived extends Base {
selam() {
return "selam";
}
}
const d = new Derived();
console.log(typeof Derived); // function
console.log(Object.getPrototypeOf(d) === Derived.prototype); // true
console.log(Object.getPrototypeOf(Derived.prototype) === Base.prototype); // true
console.log(Object.hasOwn(Derived.prototype, "selam")); // true
console.log(Object.getPrototypeOf(Derived) === Base); // trueSon satır, static üyelerin neden kalıtımla geçtiğini açıklıyor: türetilmiş sınıfın (fonksiyonun) kendi prototipi üst sınıftır. Aynı zinciri sınıf kullanmadan kurmak isterseniz MDN, Object.setPrototypeOf() ile mevcut prototip nesnesini bağlamayı önerir:
function Base() {}
Base.prototype.kim = function () {
return "Base";
};
function Derived() {
Base.call(this);
}
Object.setPrototypeOf(Derived.prototype, Base.prototype);
const o = new Derived();
console.log(o.kim()); // Base
console.log(o instanceof Base); // true
console.log(o.constructor === Derived); // trueEski kodlarda sık görülen Derived.prototype = Object.create(Base.prototype) kalıbı da çalışır, ancak prototype nesnesini değiştirdiği için constructor özelliğini kaybettirir; MDN bu yüzden onu hataya daha açık bulur.
| Yöntem | Prototip ne zaman belirlenir | Not |
|---|---|---|
| Nesne değişmezi { __proto__: p } | Oluşturma anında | Standart, kısa sözdizimi |
| Object.create(p, tanımlar) | Oluşturma anında | null prototip ve tanımlayıcı desteği |
| new Fonksiyon() | Oluşturma anında | Fonksiyon.prototype kullanılır |
| class ... extends | Oluşturma anında | En okunaklı yol, özel alan desteği |
| Object.setPrototypeOf(o, p) | Sonradan | Motor optimizasyonlarını bozabilir |
| obj.__proto__ = p | Sonradan | Standart dışı ve kullanımdan kaldırılmış |
Kendi özellik mi, miras mı?
for...in döngüsü zincirdeki tüm numaralandırılabilir özellikleri gezer; in operatörü de zincirin tamamına bakar. Yalnızca nesnenin kendi özelliklerini istiyorsanız Object.keys() veya Object.hasOwn() kullanın. Bir özelliğin değerinin undefined olması, özelliğin olmadığı anlamına gelmez; bu yüzden varlık kontrolünü değerle yapmayın.
const ust = { miras: 1 };
const alt = { kendi: 2, __proto__: ust };
for (const anahtar in alt) console.log(anahtar);
// kendi
// miras
console.log(Object.keys(alt)); // [ 'kendi' ]
console.log("miras" in alt, Object.hasOwn(alt, "miras")); // true falseinstanceof aslında zinciri sorgular
nesne instanceof F operatörü, F.prototype nesnesinin nesne'nin prototip zincirinin herhangi bir yerinde bulunup bulunmadığını test eder. Yani sorulan şey "bu nesneyi F mi oluşturdu?" değil, "F.prototype bu nesnenin zincirinde var mı?" sorusudur. Aynı kontrolü isPrototypeOf() ile de yapabilirsiniz. Bu fark, constructor hiç çalışmadan oluşturulan nesnelerde görünür hâle gelir:
class Hayvan {}
class Kedi extends Hayvan {}
const tekir = new Kedi();
console.log(tekir instanceof Hayvan); // true
console.log(Hayvan.prototype.isPrototypeOf(tekir)); // true
const sahte = Object.create(Kedi.prototype); // constructor çalışmadı
console.log(sahte instanceof Kedi); // true
console.log({} instanceof Kedi); // falseBu nedenle instanceof sonucu, nesnenin constructor'da hazırlanan alanlara sahip olduğunu garanti etmez. Sınıf örneklerini kesin olarak tanımak gerekiyorsa sınıfa özel bir alan tanımlayıp #alan in nesne kontrolü yapmak daha sağlam bir yöntemdir; bu desen class yazısında gösteriliyor.
Yaygın hatalar

- Yerleşik prototipleri genişletmek:
Array.prototype.benimMetodum = ...gibi eklemelere "monkey patching" denir. Dil ileride aynı isimle farklı davranan bir metot eklerse kodunuz bozulur. MDN tek meşru kullanımı, yeni bir özelliği eski motorlara taşıyan polyfill olarak görür. - Prototipi sonradan değiştirmek:
Object.setPrototypeOf()çalışır ama motorların zincir için yaptığı optimizasyonları bozabilir. Mümkünse prototipi oluşturma anında belirleyin. - Fonksiyon.prototype ile nesnenin prototipini karıştırmak:
Kutu.prototype,Kutufonksiyonunun prototipi değildir;Object.getPrototypeOf(Kutu)sonucuFunction.prototypeolur. - Derin zincirlerde arama maliyeti: Var olmayan bir özelliğe erişim, zincirin sonuna kadar arama yapar. Performansın kritik olduğu yerlerde zinciri gereksiz uzatmayın.
Sık Sorulan Sorular
__proto__ kullanmak doğru mu?
Nesne değişmezi içindeki { __proto__: x } anahtarı standarttır. Buna karşılık obj.__proto__ erişimcisi standart dışı ve kullanımdan kaldırılmış kabul edilir; onun yerine Object.getPrototypeOf() ve Object.setPrototypeOf() kullanın.
class kullanıyorsam prototipi bilmem gerekir mi?
Günlük kodda doğrudan prototip yazmanız gerekmeyebilir, ancak this kaybı, instanceof sonuçları veya bir metodun nereden geldiği gibi sorunları ayıklarken zinciri bilmek büyük kolaylık sağlar.
Object.create(null) ne işe yarar?
Hiçbir şey miras almayan bir nesne üretir. Kullanıcı girdisini anahtar olarak kullanan sözlüklerde, constructor veya toString gibi miras alınmış isimlerle çakışmayı önler.
Bir özelliğin nesnenin kendisine ait olduğunu nasıl anlarım?
Object.hasOwn(nesne, "ad") kullanın. Prototipi null olan nesnelerde de çalışır. Nesne metotlarının geneli için Object metotları yazısına bakabilirsiniz.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: JavaScript öğrenme rehberi · JavaScript class kullanımı · Object metotları · this anahtar kelimesi · Map, Set ve WeakMap · JavaScript nedir?