- Özellik aramasının prototip zinciri boyunca
Object.prototype'a venull'a kadar nasıl ilerlediğini açıklamak Object.createile venewile çağrılan yapıcı fonksiyonlarla nesneler oluşturmakclass'ın prototipler üzerine kurulduğunu göstermek ve kendi özellikleri kalıtılanlardan ayırt etmek
Her dizinin bir map metodu, her string'in bir toUpperCase metodu var; oysa bu metotları dizilerine hiç sen yazmadın. Peki nereden geliyorlar? JavaScript'te her nesnenin başka bir nesneye gizli bir bağlantısı vardır: prototipine. Bir özellik nesnenin kendisinde bulunmazsa JavaScript prototipe bakar. Yerleşik metotlar, kalıtım ve sınıflar bu basit mekanizmanın üzerine kuruludur.
Prototip zinciri
Bir nesnenin prototipini Object.getPrototypeOf(obj) ile okuyabilirsin (eski __proto__ yazımı da çalışır ama önerilmez). obj.x okunurken JavaScript önce nesnenin kendi özelliklerine, sonra prototipine, sonra prototipin prototipine bakar ve null'a ulaşana kadar devam eder. Özellik hiçbir yerde yoksa sonuç undefined olur. Yazma ise farklı çalışır: obj.x = ... her zaman nesnenin kendi özelliğini oluşturur ya da değiştirir, prototipe dokunmaz.
const animal = {
eats: true,
describe() {
return `${this.name} eats: ${this.eats}`;
},
};
const cat = Object.create(animal);
cat.name = 'Tom';
console.log(cat.describe());
console.log(Object.getPrototypeOf(cat) === animal);
console.log(Object.hasOwn(cat, 'name'), Object.hasOwn(cat, 'eats'));
cat.eats = false;
console.log(cat.eats, animal.eats);
console.log(Object.getPrototypeOf(animal) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype));▸ Beklenen çıktı
Tom eats: true true true false false true true null
cat.eats = false, kalıtılan özelliği “gölgeleyen” kendi özelliğini oluşturdu; animal değişmedi. Zincir: cat → animal → Object.prototype → null.Yerleşik nesneler de aynı şekilde çalışır. Bir dizinin zinciri şöyledir: dizi → Array.prototype → Object.prototype → null. map dizinin kendisinde değil, Array.prototype içindedir. in operatörü tüm zinciri, Object.hasOwn ise yalnızca kendi özellikleri kontrol eder. Array.prototype'ın kendi toString'i, Object.prototype.toString'i gölgeler.
const numbers = [1, 2, 3];
console.log(Object.getPrototypeOf(numbers) === Array.prototype);
console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype);
console.log(Object.hasOwn(numbers, 'map'), 'map' in numbers);
console.log(numbers.toString === Object.prototype.toString);
console.log(numbers.toString());▸ Beklenen çıktı
true true false true false 1,2,3
Yapıcı fonksiyonlar ve new
class ortaya çıkmadan önce aynı yapıdaki nesneler yapıcı fonksiyonlarla oluşturulurdu. Herhangi bir normal fonksiyonu new ile çağırdığında JavaScript dört adım atar:
- boş bir nesne oluşturur;
- prototipini
Fn.prototypeyapar; - fonksiyonu,
thisyeni nesne olacak şekilde çalıştırır; - fonksiyon başka bir nesne döndürmüyorsa yeni nesneyi döndürür.
Metotlar Fn.prototype'a yazılır: böylece her nesne kendi kopyasını taşımak yerine tüm örnekler her metodun tek bir kopyasını paylaşır. Bin nesneli bir programda bu gerçek bir bellek tasarrufudur.
function Student(name, grade) {
this.name = name;
this.grade = grade;
}
Student.prototype.introduce = function () {
return `${this.name}, grade ${this.grade}`;
};
const leyla = new Student('Leyla', 9);
const elvin = new Student('Elvin', 10);
console.log(leyla.introduce(), '|', elvin.introduce());
console.log(leyla.introduce === elvin.introduce);
console.log(Object.getPrototypeOf(leyla) === Student.prototype);
console.log(leyla);▸ Beklenen çıktı
Leyla, grade 9 | Elvin, grade 10
true
true
Student { name: 'Leyla', grade: 9 }Sınıflar sözdizimsel şekerdir
class (ES2015) aynı yapıyı daha okunaklı yazmanı sağlar: sınıf aslında bir fonksiyondur, metotları ClassName.prototype'a yerleşir, extends ise prototipleri birbirine bağlar. Farklar da vardır: sınıf kodu her zaman katı moddadır, sınıf new olmadan çağrılamaz, metotlar for...in içinde görünmez ve #gizli alanlar yalnızca sınıflarda bulunur.
class Shape {
constructor(name) { this.name = name; }
describe() { return `I am a ${this.name}`; }
}
class Circle extends Shape {
constructor(radius) {
super('circle');
this.radius = radius;
}
area() { return +(Math.PI * this.radius ** 2).toFixed(2); }
}
const c = new Circle(2);
console.log(typeof Shape, c.describe(), c.area());
console.log(Object.getPrototypeOf(Circle.prototype) === Shape.prototype);
console.log(Object.getOwnPropertyNames(Circle.prototype));
console.log(Object.hasOwn(c, 'area'), 'area' in c);
try {
Shape('square');
} catch (error) {
console.log(error.name);
}▸ Beklenen çıktı
function I am a circle 12.57 true [ 'constructor', 'area' ] false true TypeError
| Sınıf yazımı | Perde arkasında |
|---|---|
class A { m() {} } | function A() {} + A.prototype.m = function () {} |
class B extends A {} | Object.setPrototypeOf(B.prototype, A.prototype) |
super.m() | A.prototype.m.call(this) |
static s() {} | A.s = function () {} |
extends, statik metotların da kalıtılması için B'yi A'ya bağlar.Object.create(vehicle) ile iki nesne oluştur: car (brand: 'Toyota') ve bike (brand: 'BMX' ve kendi özelliği wheels: 2). describe() metodu ikisinde de prototipten gelmeli. Aşağıdaki satırlar sonucu yazdıracak.
const vehicle = {
wheels: 4,
describe() {
return `${this.brand} has ${this.wheels} wheels`;
},
};
// create car and bike here
console.log(car.describe());
console.log(bike.describe());
console.log(Object.hasOwn(car, 'wheels'), Object.hasOwn(bike, 'wheels'));▸ Beklenen çıktı
Toyota has 4 wheels BMX has 2 wheels false true
Counter.prototype'a bir increment() metodu ekle: value'yu 1 artırsın ve çağrıların zincirlenebilmesi için this döndürsün. Metot her nesnenin kendi özelliği değil, tüm örneklerin ortak metodu olmalı.
function Counter(start) {
this.value = start;
}
// add increment() to Counter.prototype
const a = new Counter(5);
const b = new Counter(0);
a.increment().increment();
console.log(a.value, b.value);
console.log(a.increment === b.increment);
console.log(Object.hasOwn(a, 'increment'));▸ Beklenen çıktı
7 0 true false
Önemli noktalar
- Her nesnenin prototipe gizli bir bağlantısı vardır; olmayan bir özellik zincir boyunca
Object.prototype'a, sonranull'a kadar aranır. - Bir özelliğe yazmak her zaman kalıtılanı gölgeleyen kendi özelliğini oluşturur; prototip değişmez.
Object.create(proto)verilen prototipe sahip bir nesne oluşturur;new Fn()yeni nesneyiFn.prototype'a bağlar.- Prototipteki metotları tüm örnekler paylaşır;
Object.hasOwnkendi özellikleri,inise tüm zinciri kontrol eder. classprototipler üzerine kuruludur ve ek olanaklar sunar: katı mod, zorunlunewve gizli alanlar.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
const a = { x: 1 }; const b = Object.create(a); b.x = 2; sonrasında a.x neye eşittir?