Zastanawiam się, jak najlepiej utworzyć obiekt JavaScript, który ma właściwości i metody.
Widziałem przykłady, w których osoba korzystała, var self = this
a następnie używaself.
we wszystkich funkcjach, aby upewnić się, że zakres jest zawsze poprawny.
Potem widziałem przykłady użycia .prototype
dodawania właściwości, podczas gdy inni robią to bezpośrednio.
Czy ktoś może mi podać właściwy przykład obiektu JavaScript z pewnymi właściwościami i metodami?
javascript
Michael Stum
źródło
źródło
self
jest zastrzeżone słowo? Jeśli nie, powinno być; ponieważself
jest predefiniowaną zmienną odnoszącą się do bieżącego okna.self === window
window
podobnie jak inne właściwości w Object Object w przeglądarce, takie jakdocument
lubframes
; z pewnością możesz ponownie użyć identyfikatora jako nazwy zmiennej. Chociaż tak, stylistycznie wolęvar that= this
unikać wszelkich możliwych nieporozumień. Chociażwindow.self
jest to ostatecznie bezcelowe, więc rzadko ma powód, aby go dotykać.this
do zmiennej lokalnej (np.self
) Zmniejsza rozmiary plików.Odpowiedzi:
Istnieją dwa modele implementacji klas i instancji w JavaScript: sposób prototypowania i sposób zamykania. Oba mają zalety i wady, i istnieje wiele rozszerzonych odmian. Wielu programistów i bibliotek ma różne podejścia i funkcje użytkowe do zarządzania klasami, aby opisywać niektóre bardziej brzydsze części języka.
W rezultacie w mieszanym towarzystwie będziesz mieć mieszankę metaklas, wszystkie zachowują się nieco inaczej. Co gorsza, większość materiałów instruktażowych dotyczących języka JavaScript jest okropna i służy pośredniemu kompromisowi obejmującemu wszystkie bazy, pozostawiając cię bardzo zdezorientowanym. (Prawdopodobnie autor jest również zdezorientowany. Model obiektowy JavaScript różni się bardzo od większości języków programowania, aw wielu miejscach jest źle zaprojektowany).
Zacznijmy od prototypowego sposobu . Jest to najbardziej natywny skrypt JavaScript, jaki można uzyskać: jest minimalny narzut kodu i instanceof będzie działał z instancjami tego rodzaju obiektu.
Możemy dodać metody do utworzonej instancji
new Shape
, pisząc je doprototype
wyszukiwania tej funkcji konstruktora:Teraz, aby podklasować, tyle, ile można nazwać JavaScript. Robimy to, całkowicie zastępując tę dziwną magiczną
prototype
właściwość:przed dodaniem do niego metod:
Ten przykład zadziała i zobaczysz podobny kod w wielu samouczkach. Ale stary, to
new Shape()
jest brzydkie: tworzymy instancję klasy podstawowej, nawet jeśli nie ma zostać utworzony żaden prawdziwy Kształt. Zdarza się pracy w tym prostym przypadku, ponieważ JavaScript jest tak niechlujny: pozwala zerowe argumenty mają być przekazywane w, w tym przypadkux
iy
staćundefined
i są przypisane do prototyputhis.x
ithis.y
. Gdyby funkcja konstruktora robiła coś bardziej skomplikowanego, padłaby płasko na twarz.Musimy więc znaleźć sposób na stworzenie prototypowego obiektu zawierającego metody i inne elementy, które chcemy na poziomie klasy, bez wywoływania funkcji konstruktora klasy podstawowej. Aby to zrobić, musimy zacząć pisać kod pomocniczy. To najprostsze podejście, jakie znam:
To przenosi członków klasy podstawowej w prototypie do nowej funkcji konstruktora, która nic nie robi, a następnie używa tego konstruktora. Teraz możemy napisać po prostu:
zamiast tego
new Shape()
zła. Mamy teraz akceptowalny zestaw prymitywów do zbudowanych klas.Istnieje kilka udoskonaleń i rozszerzeń, które możemy rozważyć w ramach tego modelu. Na przykład tutaj jest wersja z cukrem syntaktycznym:
Każda wersja ma tę wadę, że nie można odziedziczyć funkcji konstruktora, tak jak ma to miejsce w wielu językach. Więc nawet jeśli twoja podklasa nie wnosi nic do procesu budowy, musisz pamiętać o wywołaniu konstruktora podstawowego z dowolnymi argumentami, których żądała baza. Można to nieco zautomatyzować za pomocą
apply
, ale nadal musisz napisać:Dlatego powszechnym rozszerzeniem jest rozbicie elementów inicjalizacyjnych na ich własną funkcję, a nie na sam konstruktor. Ta funkcja może następnie dziedziczyć po podstawie:
Teraz mamy po prostu ten sam konstruktor funkcji konstruktora dla każdej klasy. Być może możemy przenieść tę funkcję do własnej funkcji pomocnika, abyśmy nie musieli jej pisać, na przykład zamiast
Function.prototype.subclass
obracać ją i pozwalać funkcji klasy podstawowej wyrzucać podklasy:... który zaczyna wyglądać trochę bardziej jak inne języki, choć z nieco nieporadną składnią. Jeśli chcesz, możesz dodać kilka dodatkowych funkcji. Może chcesz
makeSubclass
wziąć i zapamiętać nazwę klasy i podać jej domyślną nazwętoString
. Być może chcesz, aby konstruktor wykrył przypadkowe wywołanie beznew
operatora (co w innym przypadku często powodowałoby bardzo irytujące debugowanie):Być może chcesz przekazać wszystkich nowych członków i
makeSubclass
dodać ich do prototypu, aby zaoszczędzić CiClass.prototype...
dużo pisania . Robi to wiele systemów klasowych, np .:Istnieje wiele potencjalnych funkcji, które możesz uznać za pożądane w systemie obiektowym i nikt tak naprawdę nie zgadza się na jedną konkretną formułę.
Zatem sposób zamknięcia . Pozwala to uniknąć problemów związanych z dziedziczeniem opartym na prototypach JavaScript, ponieważ w ogóle nie używa się dziedziczenia. Zamiast:
Teraz każda pojedyncza instancja
Shape
będzie miała własną kopię plikutoString
metody (i dowolne inne metody lub inne elementy klasy, które dodamy).Złą rzeczą w tym, że każda instancja ma własną kopię każdego członka klasy jest to, że jest mniej wydajna. Jeśli masz do czynienia z dużą liczbą podklasowanych instancji, dziedziczenie prototypowe może ci lepiej służyć. Również wywoływanie metody klasy podstawowej jest nieco denerwujące, jak widać: musimy pamiętać, jaka była metoda, zanim konstruktor podklasy ją nadpisał, w przeciwnym razie się zgubi.
[Również dlatego, że nie ma tutaj dziedziczenia,
instanceof
operator nie będzie działał; gdybyś tego potrzebował, musisz stworzyć własny mechanizm wąchania klas. Podczas gdy ty mógł bawić obiekty prototyp w podobny sposób jak z prototypowego dziedziczenia, jest to trochę skomplikowane i naprawdę nie warto go tak aby uzyskaćinstanceof
pracę.]Zaletą każdej instancji mającej własną metodę jest to, że metoda może być następnie powiązana z konkretną instancją, która jest jej właścicielem. Jest to przydatne ze względu na dziwny sposób wiązania JavaScript
this
w wywołaniach metod, który ma efekt, że jeśli odłączysz metodę od jej właściciela:wtedy
this
w metodzie nie będzie instancji Circle zgodnie z oczekiwaniami (w rzeczywistości będzie towindow
obiekt globalny , powodujący powszechne problemy z debugowaniem). W rzeczywistości zwykle dzieje się tak, gdy metoda jest pobierana i przypisywana dosetTimeout
,onclick
lubEventListener
w ogóle.Prototypowym sposobem jest dołączenie zamknięcia dla każdego takiego zadania:
lub w przyszłości (lub teraz, jeśli włamiesz się do Function.prototype), możesz to również zrobić za pomocą
function.bind()
:jeśli twoje instancje są wykonywane w sposób zamykający, wiązanie odbywa się za darmo przez zamknięcie nad zmienną instancji (zwykle wywoływaną
that
lubself
, choć osobiście odradzam tę ostatnią, ponieważself
ma ona już inne, inne znaczenie w JavaScript). Nie otrzymujesz argumentów1, 1
z powyższego fragmentu za darmo, więc nadal będziesz potrzebować kolejnego zamknięcia lubbind()
jeśli musisz to zrobić.Istnieje również wiele wariantów metody zamknięcia. Możesz
this
całkowicie pominąć , tworząc nowythat
i zwracając go zamiastnew
operatora:Który sposób jest „właściwy”? Obie. Który jest „najlepszy”? To zależy od twojej sytuacji. FWIW Mam tendencję do tworzenia prototypów w celu rzeczywistego dziedziczenia JavaScript, gdy robię rzeczy zdecydowanie OO, i zamykania dla prostych efektów stronicowania.
Oba sposoby są jednak przeciwne do intuicji większości programistów. Oba mają wiele potencjalnych niechlujnych odmian. Poznasz oba (jak również wiele schematów pośrednich i ogólnie zepsutych), jeśli będziesz używać kodu / bibliotek innych osób. Nie ma jednej ogólnie akceptowanej odpowiedzi. Witamy w cudownym świecie obiektów JavaScript.
[Jest to część 94 Dlaczego JavaScript nie jest moim ulubionym językiem programowania.]
źródło
new
.Używam tego wzoru dość często - przekonałem się, że daje mi to dość dużą elastyczność, kiedy go potrzebuję. W użyciu jest raczej podobny do klas w stylu Java.
Używa anonimowej funkcji, która jest wywoływana podczas tworzenia, zwracając nową funkcję konstruktora. Ponieważ funkcja anonimowa jest wywoływana tylko raz, możesz tworzyć w niej prywatne zmienne statyczne (są one wewnątrz zamknięcia, widoczne dla innych członków klasy). Funkcja konstruktora jest w zasadzie standardowym obiektem Javascript - definiujesz w nim atrybuty prywatne, a atrybuty publiczne są dołączane do
this
zmiennej.Zasadniczo takie podejście łączy podejście Crockforda ze standardowymi obiektami Javascript, aby stworzyć mocniejszą klasę.
Możesz go używać tak jak każdego innego obiektu JavaScript:
źródło
this
czy nadal trzeba go utworzyćnew
?this
pojawiaconstructor
sięFoo
w przykładzie.constructor.prototype.myMethod = function () { ... }
.Douglas Crockford obszernie omawia ten temat w The Good Parts . Zaleca, aby unikać tworzenia nowego obiektu przez nowego operatora. Zamiast tego proponuje stworzenie niestandardowych konstruktorów. Na przykład:
W JavaScript funkcja jest obiektem i może być używana do konstruowania obiektów razem z nowym operatorem. Zgodnie z konwencją funkcje przeznaczone do użycia jako konstruktory zaczynają się od dużej litery. Często widzisz takie rzeczy jak:
W przypadku pominięcia użyć nowego operatora podczas uruchamianiu tego nowego obiektu, co masz jest zwykłym wywołanie funkcji, a to wiąże się z globalnego obiektu zamiast do nowego obiektu.
źródło
new
wvar that = {};
każdym razie jest to ukryte .Aby kontynuować od odpowiedzi Bobina
W es6 możesz teraz stworzyć
class
Teraz możesz zrobić:
Tak więc rozciągnij się do koła (jak w drugiej odpowiedzi), możesz:
W ES6 jest trochę czystszy i trochę łatwiejszy do odczytania.
Oto dobry przykład tego w akcji:
Pokaż fragment kodu
źródło
Możesz to również zrobić w ten sposób, używając struktur:
Następnie :
źródło
Innym sposobem może być http://jsfiddle.net/nnUY4/ (nie wiem, czy ten rodzaj obsługi obiektów tworzących i ujawniających funkcje jest zgodny z jakimś określonym wzorcem)
źródło
Kiedy używa się sztuczki zamykania „tego” podczas wywoływania konstruktora, ma to na celu napisanie funkcji, która może być wykorzystana jako wywołanie zwrotne przez inny obiekt, który nie chce wywoływać metody na obiekcie. Nie ma to związku z „poprawieniem zakresu”.
Oto waniliowy obiekt JavaScript:
Możesz wiele wyczytać z tego, co Douglas Crockford ma do powiedzenia na temat JavaScript. John Resig jest również genialny. Powodzenia!
źródło
this
ma wszystko wspólnego z „poprawieniem zakresu”.self=this
chociaż nie wymuszathis
trwałości, łatwo umożliwia „prawidłowe” ustalanie zakresu przez zamknięcie.Closure
jest wszechstronny. bobince dobrze podsumował podejście prototypowe do zamknięcia podczas tworzenia obiektów. Można jednak naśladować niektóre aspektyOOP
używania zamknięcia w funkcjonalny sposób programowania. Pamiętaj, że funkcje są obiektami w JavaScript ; więc użyj funkcji jako obiektu w inny sposób.Oto przykład zamknięcia:
Jakiś czas temu natknąłem się na artykuł Mozilli na temat Zamknięcia. Oto, co rzuca mi się w oczy: „Zamknięcie pozwala powiązać niektóre dane (środowisko) z funkcją działającą na tych danych. Ma to oczywiste podobieństwo do programowania obiektowego, w którym obiekty pozwalają nam powiązać niektóre dane (właściwości obiektu ) z jedną lub więcej metodami ”. To był pierwszy raz, kiedy czytałem paralelizm między zamknięciem a klasycznym OOP bez odniesienia do prototypu.
W jaki sposób?
Załóżmy, że chcesz obliczyć podatek VAT od niektórych produktów. Podatek VAT prawdopodobnie pozostanie stabilny przez cały okres składania wniosku. Jednym ze sposobów, aby to zrobić w OOP (pseudo kod):
Zasadniczo przekazujesz wartość VAT swojemu konstruktorowi, a twoja metoda obliczania może działać na podstawie zamknięcia . Teraz zamiast używać klasy / konstruktora, przekaż swój podatek VAT jako argument do funkcji. Ponieważ jedyne, co Cię interesuje, to samo obliczenie, zwraca nową funkcję, którą jest metoda obliczania:
W swoim projekcie określ wartości najwyższego poziomu, które są dobrymi kandydatami do tego, jaki podatek VAT jest do obliczenia. Zasadniczo za każdym razem, gdy przekazujesz te same argumenty, istnieje sposób na ulepszenie go za pomocą zamknięcia. Nie trzeba tworzyć tradycyjnych obiektów.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
źródło
Tworzenie obiektu
Najłatwiejszym sposobem utworzenia obiektu w JavaScript jest użycie następującej składni:
Działa to doskonale do przechowywania danych w uporządkowany sposób.
W przypadku bardziej złożonych przypadków użycia często lepiej jest jednak utworzyć instancje funkcji:
Umożliwia to tworzenie wielu obiektów, które mają ten sam „plan”, podobnie jak w przypadku korzystania z klas np. Jawa.
Można to jednak zrobić bardziej efektywnie, używając prototypu.
Ilekroć różne instancje funkcji współużytkują te same metody lub właściwości, możesz przenieść je do prototypu tego obiektu. W ten sposób każde wystąpienie funkcji ma dostęp do tej metody lub właściwości, ale nie musi być duplikowane dla każdego wystąpienia.
W naszym przypadku sensowne jest przeniesienie metody
f
do prototypu:Dziedzictwo
Prostym, ale skutecznym sposobem dziedziczenia w JavaScript, jest użycie następującego dwuliniowego:
Jest to podobne do robienia tego:
Główna różnica między nimi polega na tym, że konstruktor
A
nie jest uruchamiany podczas używaniaObject.create
, co jest bardziej intuicyjne i bardziej podobne do dziedziczenia klasowego.Zawsze możesz wybrać opcjonalne uruchomienie konstruktora
A
podczas tworzenia nowej instancjiB
poprzez dodanie jej do konstruktoraB
:Jeśli chcesz przekazać wszystkie argumenty
B
doA
, możesz również użyćFunction.prototype.apply()
:Jeśli chcesz wstawić inny obiekt do łańcucha konstruktorów
B
, możesz połączyćObject.create
zObject.assign
:Próbny
Uwaga
Object.create
może być bezpiecznie używany w każdej nowoczesnej przeglądarce, w tym IE9 +.Object.assign
nie działa w żadnej wersji IE ani niektórych przeglądarkach mobilnych. Zaleca się do wypełnianiaObject.create
i / lubObject.assign
jeśli chcesz ich używać i obsługiwać przeglądarki, które ich nie implementują.Możesz znaleźć polifill
Object.create
tutaj i jedenObject.assign
tutaj .źródło
źródło
Oprócz zaakceptowanej odpowiedzi z 2009 roku. Jeśli możesz atakować nowoczesne przeglądarki, możesz skorzystać z Object.defineProperty .
Aby zobaczyć listę kompatybilności z komputerami i urządzeniami mobilnymi, zobacz listę kompatybilności przeglądarki Mozilla . Tak, IE9 + obsługuje to tak samo, jak Safari Mobile.
źródło
Możesz także spróbować tego
źródło
Po pierwsze, można zmienić swoje preferencje dodanie metody do instancji zamiast konstruktora
prototype
obiektu . Prawie zawsze deklaruję metody wewnątrz konstruktora, ponieważ używam porwania konstruktora bardzo często do celów związanych z dziedziczeniem i dekoratorami.Oto jak decyduję, gdzie są zapisywane deklaracje:
this
)var
deklaracje mają pierwszeństwofunction
deklaracjami{}
i[]
)public
deklaracje mają pierwszeństwoprivate
deklaracjamiFunction.prototype.bind
nathus
,self
,vm
,etc
this
z zakresu leksykalnego obszaru zamknięcia.Oto dlaczego te pomoce:
Porwanie Konstruktora Projektowanie modeli Wzorce projektoweJak widać, naprawdę nie ma takiej potrzeby,
thus
ponieważ wolęFunction.prototype.bind
(.call
lub.apply
) więcej niżthus
. W naszejSingleton
klasie nawet nie nazywamy tego,thus
ponieważINSTANCE
przekazuje więcej informacji. WracamyModel
, abyśmythis
mogli wywołać Konstruktora,.call
aby zwrócić instancję, którą do niej przekazaliśmy. Nadmiarowo przypisaliśmy ją do zmiennejupdated
, chociaż jest przydatna w innych scenariuszach.Oprócz tego wolę konstruować literały obiektów za pomocą
Preferowane Nie preferowanenew
słowa kluczowego zamiast {nawiasów}:Jak widać, ten ostatni nie ma możliwości przesłonięcia właściwości „przesłonięcia” swojej nadklasy.
Jeśli dodam metody do
Preferowane Nie preferowaneprototype
obiektu klasy , wolę literał obiektu - z lub bez użycianew
słowa kluczowego:źródło
Chciałbym wspomnieć, że możemy użyć tytułu lub ciągu znaków, aby zadeklarować obiekt.
Istnieją różne sposoby wywoływania każdego z nich. Patrz poniżej:
źródło
Zasadniczo w JS nie ma pojęcia klasy, dlatego używamy funkcji jako konstruktora klas, który jest odpowiedni dla istniejących wzorców projektowych.
Do tej pory JS nie ma pojęcia, że chcesz utworzyć obiekt, więc oto nowe słowo kluczowe.
Ref: Professional JS dla programistów stron internetowych - Nik Z
źródło
class
w JS, jak pan wspomniał w swojej pozycji za pomocąfunction
słowa kluczowego. To nie jest wzorzec projektowy, ale celowa funkcja języka. Nie głosowałem cię w tej sprawie, ale wygląda na to, że zrobił to ktoś inny z powodu zwięzłości i niemal nieistotności pytania. Mam nadzieję, że ta opinia pomoże.