24 Ocak 2022

F.prototype

Modern JavaScript’te protitip’i __proto__ kullanarak ( bir önceki bölümde anlatıldığı gibi ) ayarlayabiliriz. Fakat bu daha öncesinde böyle değildi.

JavaScript başlangıcından beri kalıtıma sahipti. Bu dilin çekirdek özelliklerinden biriydi.

Fakat bunu ayarlamak için sadece bir yol vardı ve bu yol yapıcıda(constructor)'da "prototype" kullanmaktı. Hala birçok kodda bu şekilde kullanılmaktadır.

“Prototip” özelliği

Bildiğiniz gibi, new F() yeni bir obje oluşturur.

new F() ile yeni bir obje yaratıldığında, obje’nin [[Prototype]]F.prototype a ayarlanır.

Diğer bir deyişle, eğer F prototype özelliğine sahip ve bu da obje tipine ayarlanmışsa, new operatörü bunu [[Prototype]] ayarlamak için kullanır.

Aklınızda bulunsun F.prototype burada F'in sahip olduğu sıradan bir "prototype" objesidir. “prototype” terimine çok benzese de aslında burada gerçekten kullanılan sıradan bir objedir.

Örneğin:

let animal = {
  eats: true
};

function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype = animal;

let rabbit = new Rabbit("White Rabbit"); //  rabbit.__proto__ == animal

alert( rabbit.eats ); // true

Rabbit.prototype = animal sözcük anlamıyla: “Eğer yeni bir Rabbit yaratılırsa new Rabbit, bunun [[Prototype]]ını animal'a ata”

Sonuç şu şekildedir:

Görselde "prototip" yataydaki oktur, sıranda bir özelliktir. [[Prototype]] ise dikeydir ve rabbit'in animal'dan miras aldığını ifade eder.

Sıradan F.prototype, yapıcı (contructor) özelliği

Her fonksiyonun "prototype" özelliği bulunmaktadır. Siz belirtmeseniz bile bu geçerlidir.

Varsayılan "prototype" sadececontructor` özelliği olan olan ve bu da fonksiyonun kendisini gösteren bir objedir.

Şu şekilde:

function Rabbit() {}

/* varsayılan prototip
Rabbit.prototype = { constructor: Rabbit };
*/

Şu şekilde kontrol edebiliriz:

function Rabbit() {}
// varsayılan:
// Rabbit.prototype = { constructor: Rabbit }

alert( Rabbit.prototype.constructor == Rabbit ); // true

Eğer hiçbir şey yapmazsak doğal olarak contructor özelliği tüm rabbit objelerine [[Prototype]] vasıtasıyla iletilir.

function Rabbit() {}
// varsayılan:
// Rabbit.prototype = { constructor: Rabbit }

let rabbit = new Rabbit(); //  {constructor: Rabbit}'dan miras alır.

alert(rabbit.constructor == Rabbit); // true (prototype'tan gelir)

Eğer constructor özelliğini kullarak varolan yapıcı ile aynı şekilde bir obje yapabiliriz.

Şu şekilde:

function Rabbit(name) {
  this.name = name;
  alert(name);
}

let rabbit = new Rabbit("White Rabbit");

let rabbit2 = new rabbit.constructor("Black Rabbit");

Bir obje var fakat bu objenin ( 3. parti kütüphanelerden gelebilir) hangi yapıcısının kullanıldığını bilmiyorsak ve aynısını yaratmak istiyorsak oldukça kullanışlıdır.

Muhtemelen "contructor" hakkındaki en önemli şey…

… JavaScript "contructor" değerinin doğru olduğuna garanti vermez.

Evet, varsayılan "prototype" da bulunur fakat hepsi budur, sonrasındaki her şey bize aittir.

Daha özelde, eğer prototip’i tamamen değiştirirsek, bu durumda içinde "contructor" olmayacaktır.

Örneğin:

function Rabbit() {}
Rabbit.prototype = {
  jumps: true
};

let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false

Öyleyse doğru "contructor"'ı tutmak için varsayılan prototip’e özellik ekleme/çıkarma yoluna gidebiliriz. Tamamen üzerine yazarsak varsayılan obje kaybolur.

function Rabbit() {}

// Rabbit.prototype üzerine doğrudan yazma!
// sadece ekle
Rabbit.prototype.jumps = true
// varsayılan Rabbit.prototype.contructor bu şekilde korunacaktır.

Veya alternatif olarak, constructor özelliği tekrar yaratılabilir:

Rabbit.prototype = {
  jumps: true,
  constructor: Rabbit
};

// bu şekilde constructor doğru olur, çünkü bunu el ile belirtmekteyiz.

Özet

Bu bölümde kısaca yapıcı fonksiyonlar ile oluşturulan objelerin [[Prototip]]'lerinin nasıl ayarlanabileceğinden bahsettik. İlerde bunların daha gelişmişini programlama kalıpları üzerinde öğreneceksiniz.

Her şey aslında çok basit, birkaç cümle ile daha net anlatmak gerekirse:

  • F.prototype özelliği [[Prototype]] ile aynı değildir. F.prototype aslında new F() çağırıldığında [[Prototype]]'ı ayarlar.
  • F.prototype değeri ya obje ya da null olmalıdır: diğer değerler çalışmaz.
  • "prototype" özelliği sadece bir yapıcı fonksiyona ayarlandığı ve new ile çağırıldığında özel etkisi olur.

Normal objeler prototype'ın ayrı bir önemi yoktur:

let user = {
  name: "John",
  prototype: "Bla-bla" // hiçbir büyüsü yok
};

Varsayılan durumda tüm fonksiyonlar F.prototype = { constructor: F} şeklinde tanımlıdır, bundan dolayı, bir objenin yapıcısına "constructor" özelliği ile erişilebilir.

Görevler

önem: 5

Aşağıdaki kodda new Rabbitile yeni bir Rabbit oluşturulmuş sonra prototype’ı değiştirilmeye çalışılmıştır.

Başlangıçta aşağıdaki koda sahibiz:

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

alert( rabbit.eats ); // true
  1. Bir tane daha karakter dizisi ekledik, alert ne gösterir?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    Rabbit.prototype = {};
    
    alert( rabbit.eats ); // ?
  2. …Eğer kod aşağıdaki gibi değiştirilirse ne olur ( bir satır değiştirildi )?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    Rabbit.prototype.eats = false;
    
    alert( rabbit.eats ); // ?
  3. Ya böyle ? ( bir satır değiştirildi )

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    delete rabbit.eats;
    
    alert( rabbit.eats ); // ?
  4. Son şekli:

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    delete Rabbit.prototype.eats;
    
    alert( rabbit.eats ); // ?

Cevaplar:

  1. true.

    Rabbit.prototype ataması [[Prototype]]'I ayarlasada bu yeni objelerde etki eder. Var olanlarda bir değişikliğe neden olmaz.

  2. false.

    Objeler referanslar ile atanır. Rabbit.prototype'tan alınan obje kopya değildir, hala hem Rabbit.prototype hem de rabbit'in [[Prototype]]'ı tarafından referans edilir.

    Bundan dolayı referans edilen herhangi bir yerden içeriik değişirse bu diğerini de etkiler.

  3. true.

    Tüm delete operasyonları objeye doğrudan etki eder. Mesela delete rabbit.eats rabbit'ten eats özelliğini silmeye çalışır fakat yapaz. Bundan dolayı bu operasyonun hiçbir etkisi olayacaktır.

  4. undefined.

    eats prototip’ten silindiğinden dolayı artık bir etkisi olmayacaktır.

önem: 5

Imagine, we have an arbitrary object obj, created by a constructor function – we don’t know which one, but we’d like to create a new object using it.

Can we do it like that?

let obj2 = new obj.constructor();

Give an example of a constructor function for obj which lets such code work right. And an example that makes it work wrong.

We can use such approach if we are sure that "constructor" property has the correct value.

For instance, if we don’t touch the default "prototype", then this code works for sure:

function User(name) {
  this.name = name;
}

let user = new User('John');
let user2 = new user.constructor('Pete');

alert( user2.name ); // Pete (worked!)

It worked, because User.prototype.constructor == User.

…But if someone, so to say, overwrites User.prototype and forgets to recreate "constructor", then it would fail.

For instance:

function User(name) {
  this.name = name;
}
User.prototype = {}; // (*)

let user = new User('John');
let user2 = new user.constructor('Pete');

alert( user2.name ); // undefined

Why user2.name is undefined?

Here’s how new user.constructor('Pete') works:

  1. First, it looks for constructor in user. Nothing.
  2. Then it follows the prototype chain. The prototype of user is User.prototype, and it also has nothing.
  3. The value of User.prototype is a plain object {}, its prototype is Object.prototype. And there is Object.prototype.constructor == Object. So it is used.

At the end, we have let user2 = new Object('Pete'). The built-in Object constructor ignores arguments, it always creates an empty object – that’s what we have in user2 after all.

Eğitim haritası

Yorumlar

yorum yapmadan önce lütfen okuyun...
  • Eğer geliştirme ile alakalı bir öneriniz var ise yorum yerine github konusu gönderiniz.
  • Eğer makalede bir yeri anlamadıysanız lütfen belirtiniz.
  • Koda birkaç satır eklemek için <code> kullanınız, birkaç satır eklemek için ise <pre> kullanın. Eğer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)