Dziedziczenie, polimorfizm i enkapsulacja to trzy najbardziej wyraźne, ważne cechy OOP, a od nich dziedziczenie ma obecnie wysoką statystykę użytkowania. Uczę się JavaScript, a tutaj wszyscy mówią, że ma on dziedzictwo prototypowe, a ludzie na całym świecie mówią, że jest to coś zupełnie innego niż klasyczne dziedzictwo.
Nie rozumiem jednak, jaka jest ich różnica z praktycznego punktu widzenia? Innymi słowy, kiedy zdefiniujesz klasę bazową (prototyp), a następnie wyprowadzisz z niej niektóre podklasy, oboje masz dostęp do funkcjonalności swojej klasy bazowej i możesz rozszerzyć funkcje na klasy pochodne. Jeśli weźmiemy pod uwagę to, co powiedziałem, jako zamierzony wynik dziedziczenia, to dlaczego powinniśmy się przejmować, jeśli używamy wersji prototypowej lub klasycznej?
Aby wyjaśnić siebie bardziej, nie widzę różnicy w zakresie użyteczności i wzorców użytkowania prototypowego i klasycznego dziedziczenia. Powoduje to, że nie jestem zainteresowany dowiedzeniem się, dlaczego się różnią, ponieważ oba skutkują tym samym, OOAD. Czym praktycznie (nie teoretycznie) dziedziczenie prototypowe różni się od dziedziczenia klasycznego?
źródło
Dziedziczenie klasyczne dziedziczy zachowanie, bez żadnego stanu, od klasy nadrzędnej. Dziedziczy zachowanie w momencie tworzenia instancji obiektu.
Dziedziczenie prototypowe dziedziczy zachowanie i stan z obiektu nadrzędnego. Dziedziczy zachowanie i stan w momencie wywołania obiektu. Gdy obiekt nadrzędny zmienia się w czasie wykonywania, wpływa to na stan i zachowanie obiektów potomnych.
„Zaletą” dziedziczenia prototypowego jest to, że można „załatać” stan i zachowanie po utworzeniu instancji wszystkich obiektów. Na przykład w frameworku Ext JS często ładuje się „przesłonięcia”, które łatają główne komponenty frameworku po utworzeniu instancji frameworka.
źródło
class C(object): def m(self, x): return x*2
iinstance = C()
wtedy gdy uruchamiaminstance.m(3)
otrzymuję6
. Ale gdybym wtedy zmienićC
takC.m = lambda s, x: x*x
i biegnęinstance.m(3)
I teraz dostać9
. To samo dzieje się, jeśli zrobię aclass D(C)
i zmienię metodę,C
a następnie wszelkie przypadkiD
otrzymania zmienionej metody również. Czy mam nieporozumienie, czy to oznacza, że Python nie ma klasycznego dziedzictwa zgodnie z twoją definicją?Po pierwsze: przez większość czasu będziesz używać obiektów, a nie ich definiować, a używanie obiektów jest takie samo w obu paradygmatach.
Po drugie: Większość środowisk prototypowych używa tego samego rodzaju podziału co środowiska klasowe - zmienne dane instancji z odziedziczonymi metodami. Więc znów jest bardzo mała różnica. (Zobacz moją odpowiedź na to pytanie o przepełnienie stosu oraz programy samoorganizacji bez klas . Zajrzyj do Citeseer, aby uzyskać wersję PDF .)
Po trzecie: Dynamiczna natura Javascript ma znacznie większy wpływ niż dziedziczenie. Fakt, że mogę dodać nową metodę do wszystkich instancji typu, przypisując ją do obiektu podstawowego, jest zgrabny, ale mogę zrobić to samo w Ruby, ponownie otwierając klasę.
Po czwarte: Praktyczne różnice są niewielkie, podczas gdy praktyczna kwestia zapominania o użyciu
new
jest znacznie większa - to znaczy, że znacznie bardziej prawdopodobne jest, że będziesz odczuwać brakującenew
niż różnicę między kodem prototypowym a klasycznym .Wszystko, co powiedziano, praktyczną różnicą między dziedziczeniem prototypowym a klasycznym jest to, że twoje rzeczy, które przechowują metody (klasy) są takie same, jak twoje rzeczy, które przechowują dane (instancje). Oznacza to, że możesz budować swoje klasy fragmentarycznie, przy użyciu tych samych narzędzi do manipulacji obiektami, których można użyć w dowolnej instancji. (Tak właśnie robią wszystkie biblioteki emulujące klasy. Aby uzyskać podejście nie do końca podobne do innych, zajrzyj na Traits.js ). Jest to szczególnie interesujące w przypadku metaprogramowania.
źródło
Dziedziczenie prototypowe w JavaScript różni się od klas pod następującymi ważnymi względami:
Konstruktory to po prostu funkcje, które można wywoływać bez
new
:Nie ma prywatnych zmiennych ani metod, w najlepszym wypadku możesz to zrobić:
W poprzednim przykładzie nie można znacząco rozszerzyć klasy, jeśli uciekamy się do fałszywych zmiennych prywatnych i metod, a ponadto wszelkie zadeklarowane metody publiczne będą odtwarzane przy każdym utworzeniu nowej instancji.
źródło
color
,r
,x
,y
idrawCircle
które są związane z zakresem leksykalnymCircle
Circle.call()
jako konstruktor? Wygląda na to, że próbujesz opisać „obiekty funkcjonalne” (mylne określenie ...)