Prototypy to optymalizacja .
Świetnym przykładem ich dobrego wykorzystania jest biblioteka jQuery. Za każdym razem, gdy uzyskujesz obiekt jQuery przy użyciu $('.someClass')
, obiekt ten ma dziesiątki „metod”. Biblioteka mogłaby to osiągnąć, zwracając obiekt:
return {
show: function() { ... },
hide: function() { ... },
css: function() { ... },
animate: function() { ... },
};
Ale to oznaczałoby, że każdy obiekt jQuery w pamięci miałby dziesiątki nazwanych slotów zawierających w kółko te same metody.
Zamiast tego metody te są zdefiniowane w prototypie, a wszystkie obiekty jQuery „dziedziczą” ten prototyp, aby uzyskać wszystkie te metody przy bardzo niewielkim koszcie wykonania.
Niezwykle ważną częścią tego, jak jQuery robi to dobrze, jest to, że jest to ukryte przed programistą. Traktuje się go wyłącznie jako optymalizację, a nie jako coś, o co musisz się martwić podczas korzystania z biblioteki.
Problem z JavaScriptem polega na tym, że nagie funkcje konstruktora wymagają, aby wywołujący pamiętał, aby poprzedzić je przedrostkiem new
lub w przeciwnym razie zazwyczaj nie działają. Nie ma ku temu dobrego powodu. jQuery robi to dobrze, ukrywając ten nonsens za zwykłą funkcją $
, więc nie musisz przejmować się implementacją obiektów.
Aby można było wygodnie utworzyć obiekt z określonym prototypem, ECMAScript 5 zawiera standardową funkcję Object.create
. Znacznie uproszczona wersja wyglądałaby tak:
Object.create = function(prototype) {
var Type = function () {};
Type.prototype = prototype;
return new Type();
};
Dba o ból związany z pisaniem funkcji konstruktora, a następnie wywoływaniem jej za pomocą new
.
Kiedy unikałbyś prototypów?
Użyteczne porównanie jest z popularnymi językami OO, takimi jak Java i C #. Obsługują one dwa rodzaje dziedziczenia:
- Interfejs dziedziczenia, gdzie takie, że klasa udostępnia swój niepowtarzalny realizacji dla każdego członka interfejsu.
implement
interface
- dziedziczenie implementacji , gdzie
extend
jest class
dostarczana domyślna implementacja niektórych metod.
W JavaScript dziedziczenie prototypowe jest rodzajem dziedziczenia implementacyjnego . Tak więc w sytuacjach, w których (w C # lub Javie) wyprowadziłbyś z klasy bazowej, aby uzyskać domyślne zachowanie, do którego następnie wprowadzasz niewielkie modyfikacje za pomocą zastąpień, wtedy w JavaScript ma sens dziedziczenie prototypowe.
Jeśli jednak jesteś w sytuacji, w której używałbyś interfejsów w C # lub Javie, nie potrzebujesz żadnej konkretnej funkcji językowej w JavaScript. Nie ma potrzeby jawnego deklarowania czegoś, co reprezentuje interfejs i nie ma potrzeby oznaczania obiektów jako „implementujących” ten interfejs:
var duck = {
quack: function() { ... }
};
duck.quack();
Innymi słowy, jeśli każdy „typ” obiektu ma swoje własne definicje „metod”, to dziedziczenie z prototypu nie ma wartości. Następnie zależy to od liczby przydzielonych instancji każdego typu. Jednak w wielu projektach modułowych występuje tylko jeden egzemplarz danego typu.
W rzeczywistości wiele osób sugerowało, że dziedziczenie implementacji jest złe . Oznacza to, że jeśli istnieją pewne typowe operacje dla typu, być może jest to bardziej zrozumiałe, jeśli nie są one umieszczane w klasie podstawowej / super, ale zamiast tego są ujawniane jako zwykłe funkcje w jakimś module, do którego przekazujesz obiekt (y) chcesz na nich operować.