W pierwszej części pokazującej dziedziczenie w JavaScript, pokazałem tzw. podejście “klasyczne”, mające niby być najbardziej zbliżonym do tego, spotykanego w językach programowania posiadających silne typowanie. Tamten wpis wywołał pewne kontrowersje… Być może nie do końca dobrze wszystko wyjaśniłem, a może to ja się mylę ;) Nie jestem alfą i omegą… Jednak zostawmy już w spokoju podejście “classic” ponieważ jest ono mało szczęśliwe i najlepiej jest go w ogóle nie używać. Lepszym rozwiązaniem jest zapoznanie się i zrozumienie tego co pokażę dzisiaj! W dzisiejszym wpisie zajmiemy się bowiem czymś co znane jest pon nazwą “pożyczanie konstruktora”.

Pożyczanie konstruktora + ustawianie prototypu

Uważam, że pokazywana dziś przeze mnie technika jest dużo łatwiejsza do zrozumienia niż poprzednio pokazywany przypadek. Przede wszystkim nie stosujemy słowa kluczowego new, więc nic nie dzieje się “automagicznie” gdzieś pod spodem. Może zacznijmy od krótkiego wyjaśnienia pierwszej części tego sposobu dziedziczenia czyli pożyczania konstruktora. Oto przykładowa funkcja rodzic:

function Parent (firstName) {
    this.firstName = firstName;
}

Parent.prototype.getFirstName = function () {
    return this.firstName;
}

Na razie nic nowego - zwykła funkcja oraz dodanie funkcji do jej prototypu. Wszystko zmienia się kiedy spojrzymy na funkcję dziecka, która pożycza konstruktor:

function Child (firstName, lastName) {
  // call parent constructor
  Parent.call(this, firstName);

  this.lastName = lastName;
}

Warto zwrócić uwagę, na zaznaczoną linię numer trzy. Jest to nic innego jak jawne wywołanie funkcji (konstruktora) Parent w kontekście funkcji Child (przekazanie this jako pierwszego parametru). Utwórzmy teraz obiekt za pomocą konstruktora Child i słowa kluczowego new (czyli w stylu “klasycznym”) oraz sprawdźmy kilka rzeczy.

var child = new Child('Henryk', 'Malinowski');

alert(child.firstName);
alert(child.lastName);
alert(typeof child.getFirstName); // undefined

Jak widać, obiekt child posiada teraz zarówno odziedziczoną właściwość firstName oraz swoją własną właściwość lastName, czyli uzyskaliśmy pożądany efekt. Tutaj jedna uwaga: wywołanie call z poprzedniego przykładu spowodowało skopiowanie - właściwość firstName jest kopią a nie referencją więc nie ma ryzyka nadpisania wartości zapisanych we właściwościach rodzica! Wadą tego rozwiązania jest zaś zupełny brak dziedziczenia prototypu obiektu co jest przecież podstawą dziedziczenia w JavaScript - widać to doskonale w ostatniej zaznaczonej linii gdzie sprawdzenie typu funkcji child.getFirstName zwraca wartość undefined.

Jest jednak oczywiście sposób by temu zaradzić. Wystarczy do naszego kodu dodać jedną linię (przed wywołaniem konstruktora dziecka):

Child.prototype = new Parent();

lub lepiej:

Child.prototype = Object.create(Parent.prototype);

W ten sposób zapewniamy sobie przypisanie prototypowi dziecka obiektu prototypu rodzica (za pomocą Object.create, o której będzie jeszcze mowa w kolejnych częściach cyklu o dziedziczeniu). W tym momencie ostatnia linia z poprzedniego przykładu zacznie zwracać wartość function.

Podsumowanie

Na zakończenie oczywiście codepen, abyście mogli empirycznie sprawdzić, że to faktycznie działa:

See the Pen azGRJP by burczu (@burczu) on CodePen.

Pożyczanie konstruktora to najpopularniejszy sposób na dziedziczenie w JavaScript. W przedstawionym podejściu dziedziczone są zarówno właściwości prototypu obiektu rodzica jak i jego własne właściwości ustawione w konstruktorze z użyciem słowa kluczowego this. Należałoby się zastanowić czy to na pewno dobre podejście? Ale o tym w jednym z kolejnych wpisów.