Zalety dziedziczenia prototypowego w porównaniu z klasycznym?


271

W końcu przestałem ciągnąć stopy przez te wszystkie lata i postanowiłem nauczyć się JavaScript „poprawnie”. Jednym z najbardziej porywających elementów projektowania języków jest implementacja dziedziczenia. Mając doświadczenie w Ruby, bardzo cieszyłem się z zamykania i dynamicznego pisania; ale przez całe życie nie mogę zrozumieć, jakie korzyści można uzyskać z instancji obiektów wykorzystujących inne instancje do dziedziczenia.



Odpowiedzi:


560

Wiem, że ta odpowiedź jest spóźniona o 3 lata, ale naprawdę uważam, że obecne odpowiedzi nie dostarczają wystarczających informacji na temat tego, w jaki sposób dziedziczenie prototypowe jest lepsze niż dziedziczenie klasyczne .

Najpierw zobaczmy najczęstsze argumenty, które programiści JavaScript twierdzą w obronie dziedziczenia prototypowego (biorę te argumenty z bieżącej puli odpowiedzi):

  1. To proste.
  2. Jest potężny.
  3. Prowadzi to do mniejszego, mniej zbędnego kodu.
  4. Jest dynamiczny i dlatego jest lepszy dla dynamicznych języków.

Teraz wszystkie te argumenty są prawidłowe, ale nikt nie zadał sobie trudu, aby wyjaśnić, dlaczego. To tak, jakby powiedzieć dziecku, że nauka matematyki jest ważna. Jasne, że tak, ale dziecku na pewno to nie obchodzi; i nie możesz zrobić dziecka takiego jak matematyka, mówiąc, że to ważne.

Myślę, że problem z dziedziczeniem prototypów polega na tym, że wyjaśniono go z perspektywy JavaScript. Uwielbiam JavaScript, ale prototypowe dziedziczenie w JavaScript jest złe. W przeciwieństwie do klasycznego dziedziczenia istnieją dwa wzorce dziedziczenia prototypowego:

  1. Prototypowy wzór dziedziczenia prototypowego.
  2. Wzorzec konstruktora dziedziczenia prototypowego.

Niestety JavaScript wykorzystuje wzorzec konstruktora dziedziczenia prototypowego. Dzieje się tak dlatego, że kiedy JavaScript został utworzony, Brendan Eich (twórca JS) chciał, aby wyglądał jak Java (która ma klasyczne dziedzictwo):

Pchaliśmy go jako młodszego brata do Javy, ponieważ uzupełniającym językiem, takim jak Visual Basic, był C ++ w rodzinach językowych Microsoft w tym czasie.

Jest to złe, ponieważ kiedy ludzie używają konstruktorów w JavaScript, myślą o konstruktorach dziedziczących po innych konstruktorach. To jest źle. W prototypowym dziedzictwie obiekty dziedziczą po innych obiektach. Konstruktorzy nigdy nie pojawiają się na zdjęciu. To myli większość ludzi.

Ludzie z języków takich jak Java, która ma klasyczne dziedzictwo, stają się jeszcze bardziej zdezorientowani, ponieważ chociaż konstruktory wyglądają jak klasy, nie zachowują się jak klasy. Jak stwierdził Douglas Crockford :

Ta pośrednia intencja miała uczynić język bardziej znanym klasycznie wyszkolonym programistom, ale nie udało się tego zrobić, jak widać z bardzo niskiej opinii programistów Java o JavaScript. Wzorzec konstruktora JavaScript nie spodobał się klasycznej publiczności. Zasłonił również prawdziwy prototypowy charakter JavaScript. W rezultacie bardzo niewielu programistów wie, jak skutecznie używać języka.

Masz to. Prosto z pyska konia.

Prawdziwe dziedzictwo prototypowe

Dziedzictwo prototypowe dotyczy przede wszystkim przedmiotów. Obiekty dziedziczą właściwości z innych obiektów. To wszystko. Istnieją dwa sposoby tworzenia obiektów przy użyciu dziedziczenia prototypowego:

  1. Utwórz zupełnie nowy obiekt.
  2. Sklonuj istniejący obiekt i rozszerz go.

Uwaga: JavaScript oferuje dwa sposoby klonowania obiektu - delegowanie i konkatenacja . Odtąd będę używać słowa „klon”, aby odnosić się wyłącznie do dziedziczenia poprzez przekazanie, a słowa „kopiować”, aby odnosić się wyłącznie do dziedziczenia poprzez konkatenację.

Dość gadania. Zobaczmy kilka przykładów. Powiedz, że mam okrąg o promieniu 5:

var circle = {
    radius: 5
};

Możemy obliczyć powierzchnię i obwód koła z jego promienia:

circle.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};

circle.circumference = function () {
    return 2 * Math.PI * this.radius;
};

Teraz chcę utworzyć kolejny okrąg o promieniu 10. Jednym ze sposobów na to byłoby:

var circle2 = {
    radius: 10,
    area: circle.area,
    circumference: circle.circumference
};

Jednak JavaScript zapewnia lepszy sposób - delegowanie . Object.createFunkcja ta jest wykorzystywana w tym celu:

var circle2 = Object.create(circle);
circle2.radius = 10;

To wszystko. Właśnie zrobiłeś prototypowe dziedziczenie w JavaScript. Czy to nie było proste? Bierzesz przedmiot, klonujesz go, zmieniasz wszystko, czego potrzebujesz, i hej presto - masz nowy obiekt.

Teraz możesz zapytać: „Jak to jest proste? Za każdym razem, gdy chcę utworzyć nowy okrąg, muszę go sklonować circlei ręcznie przypisać mu promień”. Cóż, rozwiązaniem jest użycie funkcji do wykonania ciężkiego podnoszenia:

function createCircle(radius) {
    var newCircle = Object.create(circle);
    newCircle.radius = radius;
    return newCircle;
}

var circle2 = createCircle(10);

W rzeczywistości możesz to wszystko połączyć w jeden dosłowny obiekt w następujący sposób:

var circle = {
    radius: 5,
    create: function (radius) {
        var circle = Object.create(this);
        circle.radius = radius;
        return circle;
    },
    area: function () {
        var radius = this.radius;
        return Math.PI * radius * radius;
    },
    circumference: function () {
        return 2 * Math.PI * this.radius;
    }
};

var circle2 = circle.create(10);

Dziedziczenie prototypowe w JavaScript

Jeśli zauważysz w powyższym programie, createfunkcja tworzy klon circle, przypisuje radiusdo niego nowy , a następnie zwraca go. To właśnie robi konstruktor w JavaScript:

function Circle(radius) {
    this.radius = radius;
}

Circle.prototype.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};

Circle.prototype.circumference = function () {         
    return 2 * Math.PI * this.radius;
};

var circle = new Circle(5);
var circle2 = new Circle(10);

Wzorzec konstruktora w JavaScript jest odwróconym wzorcem prototypowym. Zamiast tworzyć obiekt, tworzysz konstruktor. newKluczowe wiąże thiskursor wewnątrz konstruktora do klonem prototypekonstruktora.

Brzmi myląco? Jest tak, ponieważ wzorzec konstruktora w JavaScript niepotrzebnie komplikuje rzeczy. Trudno to zrozumieć większości programistów.

Zamiast myśleć o obiektach dziedziczących po innych obiektach, myślą o konstruktorach dziedziczących po innych konstruktorach, a następnie stają się całkowicie zdezorientowani.

Istnieje wiele innych powodów, dla których należy unikać wzorca konstruktora w JavaScript. Możesz o nich przeczytać w moim blogu tutaj: Konstruktory kontra prototypy


Jakie są zatem zalety dziedziczenia prototypowego w porównaniu z dziedziczeniem klasycznym? Ponownie przejrzyjmy najczęstsze argumenty i wyjaśnijmy dlaczego .

1. Dziedziczenie prototypowe jest proste

CMS stwierdza w swojej odpowiedzi:

Moim zdaniem główną zaletą dziedziczenia prototypowego jest jego prostota.

Zastanówmy się, co właśnie zrobiliśmy. Stworzyliśmy obiekt circleo promieniu 5. Następnie sklonowaliśmy go i nadaliśmy klonowi promień 10.

Dlatego potrzebujemy tylko dwóch rzeczy, aby dziedziczenie prototypowe działało:

  1. Sposób na utworzenie nowego obiektu (np. Literały obiektu).
  2. Sposób rozszerzenia istniejącego obiektu (np Object.create.).

Natomiast klasyczne dziedziczenie jest znacznie bardziej skomplikowane. W dziedzictwie klasycznym masz:

  1. Klasy
  2. Obiekt.
  3. Interfejsy
  4. Klasy abstrakcyjne.
  5. Klasy końcowe.
  6. Wirtualne klasy podstawowe.
  7. Konstruktory
  8. Niszczyciele

Masz pomysł. Chodzi o to, że dziedziczenie prototypów jest łatwiejsze do zrozumienia, łatwiejsze do wdrożenia i łatwiejsze do uzasadnienia.

Jak pisze Steve Yegge w swoim klasycznym blogu „ Portrait of a N00b ”:

Metadane to dowolny opis lub model czegoś innego. Komentarze w twoim kodzie są po prostu opisem obliczeń w języku naturalnym. Metadane metadanych powodują, że nie są one absolutnie konieczne. Jeśli mam psa z dokumentami rodowodowymi i gubię dokumenty, nadal mam doskonale ważnego psa.

W tym samym sensie klasy są po prostu metadanymi. Klasy nie są ściśle wymagane do dziedziczenia. Jednak niektórzy ludzie (zwykle n00bs) uważają, że zajęcia są wygodniejsze w pracy. Daje im to fałszywe poczucie bezpieczeństwa.

Wiemy również, że typy statyczne to tylko metadane. To specjalistyczny rodzaj komentarza skierowany do dwóch rodzajów czytelników: programistów i kompilatorów. Typy statyczne opowiadają historię o obliczeniach, prawdopodobnie po to, by pomóc obu grupom czytelników zrozumieć intencje programu. Ale typy statyczne można wyrzucić w czasie wykonywania, ponieważ ostatecznie są to tylko stylizowane komentarze. Są jak papierkowa robota: może to sprawić, że pewien niepewny typ osobowości będzie szczęśliwszy z powodu swojego psa, ale psa na pewno to nie obchodzi.

Jak wspomniałem wcześniej, zajęcia dają ludziom fałszywe poczucie bezpieczeństwa. Na przykład NullPointerExceptionw Javie jest za dużo s, nawet jeśli kod jest doskonale czytelny. Uważam, że klasyczne dziedziczenie zwykle przeszkadza w programowaniu, ale może to tylko Java. Python ma niesamowity klasyczny system dziedziczenia.

2. Dziedziczenie prototypowe jest potężne

Większość programistów wywodzących się z klasycznego pochodzenia twierdzi, że klasyczne dziedziczenie jest potężniejsze niż dziedziczenie prototypowe, ponieważ:

  1. Zmienne prywatne.
  2. Wielokrotne dziedziczenie.

To twierdzenie jest fałszywe. Wiemy już, że JavaScript obsługuje zmienne prywatne poprzez zamknięcia , ale co z wielokrotnym dziedziczeniem? Obiekty w JavaScript mają tylko jeden prototyp.

Prawda jest taka, że ​​dziedziczenie prototypów obsługuje dziedziczenie po wielu prototypach. Dziedziczenie prototypowe oznacza po prostu, że jeden obiekt dziedziczy po innym obiekcie. Istnieją dwa sposoby implementacji dziedziczenia prototypowego :

  1. Delegacja lub dziedziczenie różnicowe
  2. Klonowanie lub konkatenacyjne dziedziczenie

Tak JavaScript pozwala jedynie na delegowanie obiektów do jednego innego obiektu. Pozwala to jednak skopiować właściwości dowolnej liczby obiektów. Na przykład _.extendwłaśnie to robi.

Oczywiście wielu programistów nie uważa tego za dziedzictwo, ponieważ instanceofi isPrototypeOfmówią inaczej. Można jednak łatwo temu zaradzić, przechowując tablicę prototypów na każdym obiekcie, który dziedziczy po prototypie przez konkatenację:

function copyOf(object, prototype) {
    var prototypes = object.prototypes;
    var prototypeOf = Object.isPrototypeOf;
    return prototypes.indexOf(prototype) >= 0 ||
        prototypes.some(prototypeOf, prototype);
}

Dlatego dziedziczenie prototypowe jest tak samo potężne jak dziedziczenie klasyczne. W rzeczywistości jest znacznie potężniejszy niż klasyczne dziedziczenie, ponieważ w dziedziczeniu prototypowym możesz ręcznie wybrać, które właściwości skopiować, a które pominąć w przypadku różnych prototypów.

W dziedziczeniu klasycznym nie można (lub przynajmniej bardzo trudno) wybrać właściwości, które chcesz dziedziczyć. Używają wirtualnych klas bazowych i interfejsów do rozwiązania problemu diamentów .

Jednak w JavaScript najprawdopodobniej nigdy nie usłyszysz o problemie z diamentem, ponieważ możesz dokładnie kontrolować, które właściwości chcesz dziedziczyć i od których prototypów.

3. Dziedziczenie prototypowe jest mniej zbędne

Ten punkt jest nieco trudniejszy do wyjaśnienia, ponieważ klasyczne dziedziczenie niekoniecznie prowadzi do bardziej zbędnego kodu. W rzeczywistości dziedziczenie, zarówno klasyczne, jak i prototypowe, stosuje się w celu zmniejszenia nadmiarowości kodu.

Jednym z argumentów może być to, że większość języków programowania z klasycznym dziedziczeniem jest typowana statycznie i wymaga od użytkownika jawnego deklarowania typów (w przeciwieństwie do Haskell, który ma niejawne typowanie statyczne). Stąd prowadzi to do bardziej pełnego kodu.

Java jest znana z tego zachowania. Wyraźnie pamiętam Boba Nystroma, który wspomniał o następującej anegdocie w swoim blogu na temat Pratta Parsera :

Musisz pokochać tutaj poziom biurokracji Java: „proszę podpisać go czterokrotnie”.

Ponownie myślę, że dzieje się tak tylko dlatego, że Java jest do bani.

Jednym z ważnych argumentów jest to, że nie wszystkie języki, które mają klasyczne dziedziczenie, obsługują wielokrotne dziedziczenie. Znowu przychodzi mi na myśl Java. Tak Java ma interfejsy, ale to nie wystarczy. Czasami naprawdę potrzebujesz wielokrotnego dziedziczenia.

Ponieważ dziedziczenie prototypowe pozwala na wielokrotne dziedziczenie, kod wymagający wielokrotnego dziedziczenia jest mniej zbędny, jeśli jest napisany przy użyciu dziedziczenia prototypowego, a nie w języku, który ma klasyczne dziedzictwo, ale nie ma wielokrotnego dziedziczenia.

4. Dziedziczenie prototypowe jest dynamiczne

Jedną z najważniejszych zalet dziedziczenia prototypów jest to, że możesz dodawać nowe właściwości do prototypów po ich utworzeniu. Umożliwia to dodawanie nowych metod do prototypu, który zostanie automatycznie udostępniony wszystkim obiektom, które delegują ten prototyp.

Nie jest to możliwe w przypadku klasycznego dziedziczenia, ponieważ po utworzeniu klasy nie można jej modyfikować w czasie wykonywania. Jest to prawdopodobnie największa zaleta dziedziczenia prototypowego w porównaniu z dziedziczeniem klasycznym i powinna być na szczycie. Lubię jednak oszczędzać to, co najlepsze na koniec.

Wniosek

Dziedziczenie prototypowe ma znaczenie. Ważne jest, aby informować programistów JavaScript, dlaczego należy porzucić konstruktorski wzorzec dziedziczenia prototypowego na rzecz prototypowego wzorca dziedziczenia prototypowego.

Musimy zacząć uczyć JavaScript poprawnie, co oznacza pokazanie nowym programistom, jak pisać kod przy użyciu wzorca prototypowego zamiast wzorca konstruktora.

Nie tylko łatwiej będzie wyjaśnić dziedziczenie prototypów za pomocą wzorca prototypowego, ale także sprawi, że będą lepsi programiści.

Jeśli podobała Ci się ta odpowiedź, powinieneś również przeczytać mój post na blogu „ Dlaczego dziedziczenie prototypowe ma znaczenie ”. Zaufaj mi, nie będziesz rozczarowany.


33
Chociaż rozumiem, skąd pochodzisz, i zgadzam się, że dziedziczenie prototypów jest bardzo przydatne, myślę, że przyjmując założenie, że „dziedzictwo prototypowe jest lepsze niż dziedziczenie klasyczne”, już jesteś skazany na porażkę. Aby dać Ci perspektywę, moja biblioteka jTypes jest klasyczną biblioteką dziedziczenia dla JavaScript. Będąc facetem, który to poświęcił, nadal będę tu siedzieć i mówić, że prototypowe dziedzictwo jest niesamowite i bardzo przydatne. Ale to tylko jedno narzędzie spośród wielu, które posiada programista. Nadal istnieje również wiele wad dziedziczenia prototypowego.
redline

7
Całkowicie zgadzam się z tym, co powiedziałeś. Uważam, że zbyt wielu programistów odrzuca JavaScript z powodu braku klasycznego dziedziczenia, lub potępia go jako prosty i głupi język. Zgadzam się, że jest to niezwykle potężna koncepcja i wielu programistów powinno ją zaakceptować i nauczyć się. Biorąc to pod uwagę, czuję też, że istnieje znaczna liczba programistów w JavaScript, którzy sprzeciwiają się jakiejkolwiek formie klasycznego dziedziczenia razem w JavaScript, kiedy tak naprawdę nie mają podstaw do swoich argumentów. Oba są równie potężne same w sobie i równie przydatne.
redline

9
Cóż, to jest twoja opinia, ale nadal się nie zgadzam i myślę, że rosnąca popularność takich rzeczy jak CoffeeScript i TypeScript pokazuje, że istnieje duża społeczność programistów, którzy chcieliby skorzystać z tej funkcji, gdy jest to właściwe. Jak powiedziałeś, ES6 dodaje cukier syntaktyczny, ale nadal nie oferuje rozszerzalności jTypes. Na marginesie, nie jestem odpowiedzialny za twoje zdanie. Chociaż nie zgadzam się z tobą, nie wydaje mi się, że masz złą odpowiedź. Byłeś bardzo dokładny.
redline

25
Często używasz klonowania słów , co jest po prostu błędne. Object.createtworzy nowy obiekt z określonym prototypem. Wybór słów sprawia wrażenie, że prototyp zostaje sklonowany.
Pavel Horal

7
@Atit: naprawdę nie trzeba być tak defensywnym. Twoja odpowiedź jest bardzo szczegółowa i zasługuje na głosy. Nie sugerowałem, że „połączone” powinno zastępować „klon”, ale bardziej odpowiednio opisuje połączenie między obiektem a prototypem, z którego dziedziczy, bez względu na to, czy podasz własną definicję terminu „klon” " albo nie. Zmień to lub nie zmieniaj, to całkowicie twój wybór.
Andy E

42

Pozwól mi faktycznie odpowiedzieć na pytanie w tekście.

Dziedziczenie prototypów ma następujące zalety:

  1. Jest lepiej dostosowany do języków dynamicznych, ponieważ dziedziczenie jest tak dynamiczne jak środowisko, w którym się znajduje. (Możliwość zastosowania JavaScript powinna być tutaj oczywista). To pozwala ci robić rzeczy w locie, takie jak dostosowywanie klas bez ogromnej ilości kodu infrastruktury .
  2. Łatwiej jest wdrożyć prototypowy schemat obiektów niż klasyczne schematy dychotomii klas / obiektów.
  3. Eliminuje to potrzebę stosowania złożonych ostrych krawędzi wokół modelu obiektowego, takich jak „metaklasy” (nigdy nie lubiłem metaklasy ... przepraszam!) Lub „wartości własne” lub podobne.

Ma jednak następujące wady:

  1. Sprawdzanie typu prototypowego języka nie jest niemożliwe, ale jest bardzo, bardzo trudne. Większość „sprawdzania typów” języków prototypowych to sprawdzanie stylu „kaczego pisania” w czasie rzeczywistym. Nie jest to odpowiednie dla wszystkich środowisk.
  2. Podobnie trudno jest zrobić takie rzeczy, jak optymalizacja wysyłki metod metodą analizy statycznej (a często nawet dynamicznej!). To może (podkreślam: może ) być bardzo nieefektywne bardzo łatwo.
  3. Podobnie tworzenie obiektów może być (i zwykle jest) znacznie wolniejsze w języku prototypowym niż w bardziej konwencjonalnym schemacie dychotomii klas / obiektów.

Myślę, że możesz czytać między wierszami powyżej i wymyślić odpowiednie zalety i wady tradycyjnych schematów klas / obiektów. W każdym obszarze jest oczywiście więcej, więc resztę pozostawiam innym osobom odpowiadającym.


1
Hej, zwięzła odpowiedź, która nie jest fanboyem. Naprawdę chciałbym, żeby to była najlepsza odpowiedź na pytanie.
siliconrockstar

Obecnie mamy dynamiczne kompilatory Just-in-time, które mogą kompilować kod podczas działania kodu, tworząc różne fragmenty kodu dla każdej sekcji. JavaScript jest w rzeczywistości szybszy niż Ruby lub Python, które używają klasycznych klas z tego powodu, nawet jeśli używasz prototypów, ponieważ wiele pracy zostało zrobione przy jego optymalizacji.
aoeu256

28

IMO główną zaletą dziedziczenia prototypowego jest jego prostota.

Prototypowa natura języka może dezorientować ludzi, którzy są klasycznie wyszkoleni, ale okazuje się, że w rzeczywistości jest to naprawdę prosta i potężna koncepcja, dziedziczenie różnicowe .

Nie musisz dokonywać klasyfikacji , twój kod jest mniejszy, mniej zbędny, obiekty dziedziczą po innych, bardziej ogólnych obiektach.

Jeśli myślisz prototypowo , wkrótce zauważysz, że nie potrzebujesz zajęć ...

Dziedziczenie prototypów będzie znacznie bardziej popularne w najbliższej przyszłości, specyfikacja ECMAScript 5th Edition wprowadziła Object.createmetodę, która pozwala na stworzenie nowej instancji obiektu, która dziedziczy po innej w naprawdę prosty sposób:

var obj = Object.create(baseInstance);

Ta nowa wersja standardu jest wdrażana przez wszystkich dostawców przeglądarek i myślę, że zaczniemy widzieć bardziej czyste dziedziczenie prototypów ...


11
„Twój kod jest mniejszy, mniej zbędny ...”, dlaczego? Przejrzałem link do Wikipedii dla „dziedziczenia różnicowego” i nic nie potwierdza tych twierdzeń. Dlaczego klasyczne dziedziczenie miałoby skutkować większym, bardziej zbędnym kodem?
Noel Abrahams,

4
Dokładnie zgadzam się z Noelem. Dziedziczenie prototypowe jest po prostu jednym ze sposobów wykonania pracy, ale nie jest to właściwy sposób. Różne narzędzia będą działać na różne sposoby dla różnych zadań. Dziedzictwo prototypowe ma swoje miejsce. To niezwykle potężna i prosta koncepcja. Biorąc to pod uwagę, brak wsparcia dla prawdziwej enkapsulacji i polimorfizmu stawia JavaScript w znacznej niedogodności. Metodologie te istniały o wiele dłużej niż JavaScript, i są solidne w swoich zasadach. Zatem myślenie, że prototyp jest „lepszy”, jest po prostu niewłaściwą mentalnością.
redline

1
Można symulować dziedziczenie oparte na klasach za pomocą dziedziczenia opartego na prototypach, ale nie odwrotnie. To może być dobry argument. Widzę także enkapsulację bardziej jako konwencję niż funkcję języka (zwykle można przerwać enkapsulację poprzez odbicie). Jeśli chodzi o polimorfizm - wszystko, co zyskujesz, to nie musisz pisać prostych warunków „jeśli” podczas sprawdzania argumentów metody (i odrobinę prędkości, jeśli metoda docelowa zostanie rozwiązana podczas kompilacji). Nie ma tutaj prawdziwych wad JavaScript.
Pavel Horal

Prototypy są niesamowite, IMO. Zastanawiam się nad stworzeniem funkcjonalnego języka, takiego jak Haskell ... ale zamiast tworzyć abstrakcje oprę wszystko na prototypach. Zamiast uogólniać sumę i silnie „spasować” powinieneś prototyp funkcji sumy i zastąpić + *, a 0 1, aby otrzymać produkt. Wyjaśnię „Monady” jako prototypy, które zastępują „wtedy” od obietnic / wywołań zwrotnych, a flatMap jest synonimem „wtedy”. Myślę, że prototypy mogą pomóc w wprowadzeniu masowego programowania funkcjonalnego.
aoeu256,

11

Tak naprawdę nie ma wiele do wyboru między tymi dwiema metodami. Podstawową ideą do uchwycenia jest to, że gdy silnik JavaScript otrzymuje właściwość obiektu do odczytu, najpierw sprawdza instancję, a jeśli tej właściwości brakuje, sprawdza łańcuch prototypów. Oto przykład, który pokazuje różnicę między prototypowym a klasycznym:

Prototypowy

var single = { status: "Single" },
    princeWilliam = Object.create(single),
    cliffRichard = Object.create(single);

console.log(Object.keys(princeWilliam).length); // 0
console.log(Object.keys(cliffRichard).length); // 0

// Marriage event occurs
princeWilliam.status = "Married";

console.log(Object.keys(princeWilliam).length); // 1 (New instance property)
console.log(Object.keys(cliffRichard).length); // 0 (Still refers to prototype)

Klasyczny z metodami instancji (nieefektywne, ponieważ każda instancja przechowuje swoją własność)

function Single() {
    this.status = "Single";
}

var princeWilliam = new Single(),
    cliffRichard = new Single();

console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 1

Wydajny klasyk

function Single() {
}

Single.prototype.status = "Single";

var princeWilliam = new Single(),
    cliffRichard = new Single();

princeWilliam.status = "Married";

console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 0
console.log(cliffRichard.status); // "Single"

Jak widać, ponieważ można manipulować prototypem „klas” zadeklarowanych w stylu klasycznym, korzystanie z dziedziczenia prototypowego naprawdę nie przynosi korzyści. Jest to podzbiór metody klasycznej.


2
Patrząc na inne odpowiedzi i zasoby na ten temat, twoja odpowiedź wydaje się brzmieć: „Dziedziczenie prototypowe jest podzbiorem cukru syntaktycznego dodanego do JavaScript, aby umożliwić pojawienie się klasycznego dziedzictwa”. OP wydaje się pytać o korzyści wynikające z dziedziczenia prototypowego w JS w porównaniu z dziedziczeniem klasycznym w innych językach zamiast porównywania technik tworzenia instancji w JavaScript.
Fader Darkly

2

Tworzenie stron internetowych: dziedziczenie prototypowe a dziedziczenie klasyczne

http://chamnapchhorn.blogspot.com/2009/05/prototypal-inheritance-vs-classical.html

Prototypowe dziedziczenie klasycznych V - przepełnienie stosu

Prototypowe dziedzictwo klasycznego Vs


20
Myślę, że lepiej jest podsumować zawartość linków niż wkleić link (coś, co sam zrobiłem), chyba że jest to kolejny link SO. Wynika to z tego, że linki / witryny ulegają awarii i użytkownik traci odpowiedź na pytanie, co potencjalnie wpływa na wyniki wyszukiwania.
James Westgate

Pierwszy link nie odpowiada na pytanie, dlaczego dziedziczenie prototypowe? Po prostu to opisuje.
viebel,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.