Kurs obiektowego JavaScript (22)

Autor: Damian Chodorek • Opublikowany: 10 października 2014 • Ostatnia aktualizacja: 8 lutego 2015 • Kategoria: javascript, kursy

Model programowania oparty o prototyp.

W tym artykule poznasz bardzo ważną własność języka JavaScript. Własność, która odróżnia ten język od innych, popularnych jak C++, Java czy Python. JS jest językiem, w którym obiektowość jest oparta o prototyp. Oznacza to, że dziedziczenie odbywa się na podstawie jakiegoś prototypu, a nie klasy.

Jak pamiętasz obiekty dziedziczą po obiektach. Nie ma klas. Ten artykuł pozwoli Ci zrozumieć mechanizm działania obiektowości w JS. To jedna z tych rzeczy, której zrozumienie sprawia, że wszystko staje się jasne. Początkowo jednak, informacje mogą wydać się nieintuicyjne.

Własność prototype

Jak pamiętasz z poprzednich części, funkcje to także obiekty. W momencie ich definicji automatycznie zostają utworzone różne metody i pola domyślne. Jednym z takich pól jest prototype i początkowo zawiera pusty obiekt.

function f(){}
typeof f.prototype; //"object"

Do prototype możesz dodawać funkcje oraz pola. Nie będą mieć szczególnego znaczenia z punktu widzenia funkcji, chyba że zostanie użyta jako konstruktor.

Dodawanie pól i metod do prototype

function Thing(name, weight){
  this.weight=weight;
  this.name=name;
  this.description=function(){
    return "This is "+this.name+" and it weights "+this.weight;
  }
}

Jeśli do obiektu prototype tej funkcji zostaną dodane pola i metody, to wszystkie obiekty stworzone przy jej pomocy, będą je także posiadać.

Thing.prototype.price=100;
Thing.prototype.getPrice=function(){ return this.price; }

//poniżej inny sposób na dodanie pól i metod:
Thing.prototype={
  price: 100,
  getPrice: function(){ return this.price; }
}

Jak więc widzisz, sposoby są dwa. Można dodawać pojedynczo pola i metody lub przypisać do prototype całkowicie nowy obiekt.

Korzystanie z prototype

Wszystko co zostało dodane do pola prototype będzie widoczne w każdym obiekcie utworzonym przy pomocy danego konstruktora.

var chair=new Thing('chair', 10);
chair.name; //"chair"
chair.weight; //10
chair.description(); //This is chair and it weights 10"

chair.price; //100
chair.getPrice(); //100

Uwaga! Pola i metody z prototype nie są kopiowane do utworzonych obiektów. Przekazywana jest jedynie referencja. To znaczy, że modyfikacja w jednym obiekcie będzie widziana w pozostałych!

Thing.prototype.color="brown";

//mimo, że zmienna chair utworzona była przed zmianą, to ma dostęp do nowego pola!
chair.color; //"brown"

Oto jak działa ten mechanizm:

  • interpreter JS napotyka konstrukcję chair.name,
  • pierwszą rzeczą jaką zrobi, będzie przejrzenie obiektu chair w poszukiwaniu pola name,
  • jeśli pole zostanie znalezione, to będzie wykorzystane,
  • jeśli nie będzie odnalezione, to silnik JS przeszuka pola prototype, znajdującego się w konstruktorze obiektu czyli chair.constructor.prototype.name,
  • jeśli tam pole zostanie odnalezione, będzie użyte.

Skoro prototype też jest obiektem, to również posiada swój konstruktor, który z kolei także posiada prototype. Poprawna jest więc konstrukcja chair.constructor.prototype.constructor.prototype.constructor.prototype w zależności od długości łańcucha prototypów. Ostatecznie dostaniesz wbudowany obiekt Object, który jest rodzicem pozostałych. Z tego powodu chair.toString() zadziała. Metoda toString() zawarta jest w obiekcie Object, do którego pozostałe obiekty mogą się odnieść poprzez constructor.prototype//....

Przesłanianie pól prototype

Co się jednak stanie, gdy utworzony obiekt będzie posiadał pole o tej samej nazwie co pole w prototype konstruktora? Zgodnie z tym czego dowiedziałeś się niedawno – będzie ono ważniejsze.

function Thing(name){
  this.name=name; //będzie polem obiektu
}
Thing.prototype.name="Thing"; //to własność prototype

var chair=new Thing("chair");
chair.name; //"chair"

//jeśli usuniemy pole obiektu, zostanie użyte to z prototype
delete chair.name;
chair.name; //"Thing"

//jeśli utworzysz pole na nowo, znów będzie najważniejsze
chair.name="white chair";
chair.name; //"white chair"

W tym artykule to wszystko. Przejdź do następnej części:

Kurs obiektowego JavaScript (23)

Zobacz również
Kurs obiektowego JavaScript (20)Dokładne omówienie wyrażeń regularnych.
Kurs obiektowego JavaScript (21)Mechanizm obsługi błędów.
Kurs obiektowego JavaScript (23)Wyliczeniowe pola obiektów.
Kurs obiektowego JavaScript (24)Rozszerzanie funkcjonalności obiektów wbudowanych.

1 komentarz

  • Adrian napisał(a):

    Czy czasami chair.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype nie idzie w nieskończoność, bo są wzajemnie wskazujące sie referencje i nie dojdzie nigdy do Object?

  • Dodaj komentarz

    Twój adres e-mail nie zostanie opublikowany.