Jaka jest różnica pomiędzy
var A = function () {
this.x = function () {
//do something
};
};
i
var A = function () { };
A.prototype.x = function () {
//do something
};
javascript
prototype
this
sw234
źródło
źródło
a1.x !== a2.x
:; na prototypie:a1.x === a2.x
Odpowiedzi:
Przykłady mają bardzo różne wyniki.
Przed spojrzeniem na różnice należy zwrócić uwagę na następujące kwestie:
[[Prototype]]
własności instancji .myObj.method()
), To w metodzie odwołuje się do obiektu. Gdzie to nie jest ustawione przez wezwanie lub przez zastosowanie wiążą , to domyślnie globalnego obiektu (okno w przeglądarce) lub w trybie ścisłym, pozostaje niezdefiniowana.Oto fragmenty, o których mowa:
W tym przypadku zmiennej
A
przypisuje się wartość, która jest odwołaniem do funkcji. Gdy ta funkcja jest wywoływana przy użyciuA()
funkcja jest to nie jest ustawiony przez wywołanie więc domyślnie globalnego obiektu i wyrażeniethis.x
jest skutecznawindow.x
. W rezultacie odniesienie do wyrażenia funkcyjnego po prawej stronie jest przypisanewindow.x
.W przypadku:
dzieje się coś zupełnie innego. W pierwszym wierszu zmiennej
A
przypisuje się odwołanie do funkcji. W JavaScript wszystkie obiekty funkcji mają domyślnie właściwość prototypu, więc nie ma osobnego kodu do utworzenia obiektu A.prototype .W drugim wierszu A.prototype.x jest przypisane odwołanie do funkcji. Spowoduje to utworzenie właściwości x, jeśli nie istnieje, lub przypisanie nowej wartości, jeśli tak jest. Różnica w stosunku do pierwszego przykładu, w którym właściwość x obiektu jest zaangażowana w wyrażenie.
Kolejny przykład znajduje się poniżej. Jest podobny do pierwszego (i może o co chciałeś zapytać):
W tym przykładzie
new
operator został dodany przed wyrażeniem funkcji, dzięki czemu funkcja jest wywoływana jako konstruktor. Kiedy wywołananew
funkcja jest ten ustawiony jest odwoływać się do nowego obiektu, którego prywatna[[Prototype]]
własność jest ustawiona odwołać konstruktora publicznego prototypu . Tak więc w instrukcji przypisaniax
właściwość zostanie utworzona na tym nowym obiekcie. Funkcja wywoływana jako konstruktor domyślnie zwraca ten obiekt, więc nie ma potrzeby oddzielnejreturn this;
instrukcji.Aby sprawdzić, czy A ma właściwość x :
Jest to rzadkie użycie nowego, ponieważ jedynym sposobem na odniesienie się do konstruktora jest A.constructor . O wiele bardziej powszechne byłoby:
Innym sposobem osiągnięcia podobnego wyniku jest użycie natychmiast wywołanego wyrażenia funkcyjnego:
W takim przypadku
A
przypisano wartość zwrotną wywołania funkcji po prawej stronie. Tutaj ponownie, ponieważ to nie jest ustawiony w zaproszeniu, będzie odnosić się do globalnego obiektu ithis.x
jest skutecznywindow.x
. Ponieważ funkcja nic nie zwraca,A
będzie miała wartośćundefined
.Te różnice między tymi dwoma podejściami ujawniają się również, gdy serializujesz i usuwasz serializację obiektów JavaScript do / z JSON. Metody zdefiniowane w prototypie obiektu nie są serializowane podczas serializacji obiektu, co może być wygodne, gdy na przykład chcesz serializować tylko części danych obiektu, ale nie są to metody:
Powiązane pytania :
Sidenote: Pomiędzy tymi dwoma podejściami może nie być znacznych oszczędności pamięci, jednak użycie prototypu do udostępniania metod i właściwości prawdopodobnie zużyje mniej pamięci niż każda instancja posiadająca własną kopię.
JavaScript nie jest językiem niskiego poziomu. Myślenie o prototypowaniu lub innych wzorcach dziedziczenia jako sposobie jawnej zmiany sposobu przydzielania pamięci może nie być bardzo cenne.
źródło
null
), ale to bardzo różni się odprototype
właściwości - która dotyczy funkcji i do której jest ustawiany prototyp wszystkich instancji, gdy są one zbudowanenew
. Nie mogę uwierzyć, że to naprawdę zyskało 87 głosów pozytywnych :-("The language is functional"
czy jesteś pewien, że to właśnie oznacza funkcjonalność?A
jako funkcji, a druga połowa dotyczy niejasnych i niekonwencjonalnych sposobów działania coś prostego.Jak powiedzieli inni, pierwsza wersja, użycie „this” powoduje, że każda instancja klasy A ma własną niezależną kopię metody funkcji „x”. Natomiast użycie „prototypu” oznacza, że każde wystąpienie klasy A będzie używać tej samej kopii metody „x”.
Oto kod pokazujący tę subtelną różnicę:
Jak wspomnieli inni, istnieją różne powody, aby wybrać jedną lub drugą metodę. Moja próbka ma jedynie wyraźnie pokazać różnicę.
źródło
this
obiektu, który jest właścicielem metody. tzn. metoda nie ma obiektu będącego jej właścicielem. W tym przypadku istniejethis
obiekt, jak pokazano w klasie A w przykładzie.Weź te 2 przykłady:
vs.
Większość osób (szczególnie najwyżej oceniane odpowiedzi) próbowała wyjaśnić, czym się różnią, nie wyjaśniając DLACZEGO. Myślę, że to źle i jeśli najpierw zrozumiesz podstawy, różnica stanie się oczywista. Spróbujmy najpierw wyjaśnić podstawy ...
a) Funkcja jest obiektem w JavaScript. KAŻDY obiekt w JavaScript otrzymuje właściwość wewnętrzną (co oznacza, że nie możesz uzyskać do niej dostępu tak jak inne właściwości, z wyjątkiem być może przeglądarek takich jak Chrome), często nazywanych
__proto__
(możesz tak naprawdę wpisaćanyObject.__proto__
w Chrome, aby zobaczyć, do czego się odwołuje). , właściwość, nic więcej. Właściwość w JavaScript = zmienna wewnątrz obiektu, nic więcej. Co robią zmienne? Wskazują na rzeczy.Więc na co wskazuje ta
__proto__
właściwość? Cóż, zwykle inny przedmiot (wyjaśnimy później). Jedynym sposobem wymuszenia JavaScript dla__proto__
właściwości NIE wskazywania innego obiektu jest użycievar newObj = Object.create(null)
. Nawet jeśli to zrobisz,__proto__
właściwość STILL istnieje jako właściwość obiektu, po prostu nie wskazuje innego obiektu, wskazujenull
.Oto, gdzie większość ludzi się myli:
Kiedy tworzysz nową funkcję w JavaScript (która jest również obiektem, pamiętasz?), W momencie jej zdefiniowania JavaScript automatycznie tworzy nową właściwość dla tej funkcji o nazwie
prototype
. Spróbuj:A.prototype
jest CAŁKOWICIE RÓŻNY od__proto__
nieruchomości. W naszym przykładzie „A” ma teraz DWIE właściwości zwane „prototypem” i__proto__
. To duże zamieszanie dla ludzi.prototype
a__proto__
właściwości nie są w żaden sposób powiązane, są oddzielnymi rzeczami wskazującymi na osobne wartości.Możesz się zastanawiać: dlaczego JavaScript ma
__proto__
właściwość utworzoną na każdym obiekcie? Cóż, jedno słowo: delegacja . Kiedy wywołujesz właściwość obiektu, a obiekt go nie ma, JavaScript szuka obiektu, do którego się odwołuje,__proto__
aby sprawdzić, czy może go mieć. Jeśli go nie ma, sprawdza__proto__
właściwość tego obiektu i tak dalej ... aż do końca łańcucha. Stąd nazwa łańcucha prototypów . Oczywiście, jeśli__proto__
nie wskazuje na obiekt, a zamiast tego wskazuje nanull
szczęście, JavaScript zdaje sobie z tego sprawę i zwróci ciundefined
własność.Możesz się również zastanawiać, dlaczego JavaScript tworzy właściwość wywoływaną
prototype
dla funkcji podczas jej definiowania? Ponieważ próbuje cię oszukać, tak oszuka cię , że działa jak języki oparte na klasach.Przejdźmy do naszego przykładu i stwórzmy „obiekt” z
A
:Coś się dzieje w tle, kiedy to się stało.
a1
jest zwykłą zmienną, której przypisano nowy, pusty obiekt.Fakt, że użyłeś operatora
new
przed wywołaniem funkcji,A()
zrobił coś DODATKOWEGO w tle. Słowonew
kluczowe utworzyło nowy obiekt, który teraz się odwołuje,a1
a ten obiekt jest pusty. Oto, co dzieje się dodatkowo:Powiedzieliśmy, że w każdej definicji funkcji jest utworzona nowa właściwość o nazwie
prototype
(do której można uzyskać do niej dostęp, w przeciwieństwie do tej__proto__
właściwości)? Cóż, ta właściwość jest teraz używana.Jesteśmy teraz w punkcie, w którym mamy świeżo upieczony pusty
a1
przedmiot. Powiedzieliśmy, że wszystkie obiekty w JavaScript mają wewnętrzną__proto__
właściwość, która wskazuje na coś (a1
również to ma), czy to null, czy inny obiekt. Conew
operator nie jest to, że ustawia tę__proto__
właściwość, aby wskazywała funkcję wprototype
nieruchomości. Przeczytaj to jeszcze raz. Zasadniczo jest to:Powiedzieliśmy, że
A.prototype
to nic więcej niż pusty obiekt (chyba że zmienimy go na coś innego przed zdefiniowaniema1
). Więc teraz w zasadziea1.__proto__
wskazuje na to samoA.prototype
, co ten pusty obiekt. Oba wskazują na ten sam obiekt, który został utworzony, gdy ta linia się wydarzyła:Teraz podczas
var a1 = new A()
przetwarzania instrukcji dzieje się coś innego . ZasadniczoA()
jest wykonywany, a jeśli A jest coś takiego:Wszystkie te rzeczy w środku
function() { }
zostaną wykonane. Gdy dojdziesz dothis.hey..
linii,this
zmieni się naa1
i otrzymasz:Nie będę wyjaśniać, dlaczego
this
wprowadzono zmiany,a1
ale jest to świetna odpowiedź, aby dowiedzieć się więcej.Podsumowując, kiedy to zrobisz,
var a1 = new A()
w tle dzieją się 3 rzeczy:a1
.a1 = {}
a1.__proto__
właściwość jest przypisana do wskazywania tego samego, coA.prototype
wskazuje na (inny pusty obiekt {})Funkcja
A()
jest wykonywana zthis
ustawionym nowym, pustym obiektem utworzonym w kroku 1 (przeczytaj odpowiedź, do której się odwoływałem powyżej, dlaczegothis
wprowadzono zmianya1
)Teraz spróbujmy stworzyć inny obiekt:
Kroki 1, 2, 3 powtórzą się. Czy coś zauważyłeś? Kluczowym słowem jest powtórzenie. Krok 1:
a2
będzie nowym pustym obiektem, krok 2: jego__proto__
właściwość wskaże tę samą rzecz,A.prototype
a co najważniejsze, krok 3: funkcjaA()
zostanie PONOWNIE wykonana, co oznacza, żea2
otrzymahey
właściwość zawierającą funkcję.a1
ia2
nazwijmy dwie ODDZIELNE właściwości,hey
które wskazują na 2 ODDZIELNE funkcje! Mamy teraz zduplikowane funkcje w tych samych dwóch różnych obiektach, które robią to samo, ups ... Możesz sobie wyobrazić implikacje pamięciowe, jeśli utworzymy 1000 obiektównew A
, po tym, jak deklaracje wszystkich funkcji zajmują więcej pamięci niż coś takiego jak liczba 2. Więc jak temu zapobiec?Pamiętasz, dlaczego
__proto__
właściwość istnieje na każdym obiekcie? Jeśli więc pobierzeszyoMan
właściwośća1
(która nie istnieje),__proto__
zostanie sprawdzona jej właściwość, która, jeśli jest to obiekt (i w większości przypadków tak jest), sprawdzi, czy zawierayoMan
, a jeśli nie, sprawdzi ten obiekt__proto__
itp. Jeśli to zrobi, weźmie tę wartość właściwości i wyświetli ją.Więc ktoś postanowił użyć tego faktu + faktu, że podczas tworzenia
a1
jego__proto__
właściwość wskazuje na ten sam (pusty) obiektA.prototype
i robi to:Fajne! Teraz, kiedy tworzysz
a1
, ponownie przechodzi przez wszystkie 3 powyższe kroki, aw kroku 3 nic nie robi, ponieważfunction A()
nie ma nic do wykonania. A jeśli zrobimy:Zobaczy, że
a1
nie zawiera,hey
i sprawdzi swój__proto__
obiekt właściwości, aby zobaczyć, czy go ma, co ma miejsce.Dzięki takiemu podejściu eliminujemy część z kroku 3, w której funkcje są powielane przy każdym tworzeniu nowego obiektu. Zamiast
a1
ia2
posiadające odrębnąhey
własność, teraz żaden z nich nie ma. Co, jak sądzę, do tej pory sam się zorientowałeś. To miła rzecz ... jeśli zrozumiesz,__proto__
iFunction.prototype
takie pytania będą dość oczywiste.UWAGA: Niektóre osoby zwykle nie nazywają wewnętrznej właściwości Prototype, ponieważ
__proto__
użyłem tej nazwy pocztą, aby wyraźnie odróżnić ją odFunctional.prototype
właściwości jako dwie różne rzeczy.źródło
__proto__
i.prototype
są zupełnie inne rzeczy.W większości przypadków są one zasadniczo takie same, ale druga wersja oszczędza pamięć, ponieważ istnieje tylko jedno wystąpienie funkcji zamiast oddzielnej funkcji dla każdego obiektu.
Powodem korzystania z pierwszego formularza jest dostęp do „członków prywatnych”. Na przykład:
Ze względu na reguły zakresu javascript, private_var jest dostępne dla funkcji przypisanej do this.x, ale nie poza obiektem.
źródło
Pierwszy przykład zmienia interfejs tylko dla tego obiektu. Drugi przykład zmienia interfejs dla wszystkich obiektów tej klasy.
źródło
x
dla wszystkich obiektów, których prototypowi przypisano nową instancję A:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
Ostatecznym problemem związanym z używaniem
this
zamiast tegoprototype
jest to, że podczas przesłonięcia metody konstruktor klasy podstawowej nadal będzie odwoływał się do przesłoniętej metody. Rozważ to:przeciw:
Jeśli uważasz, że to nie jest problem, zależy to od tego, czy możesz żyć bez zmiennych prywatnych i czy masz wystarczające doświadczenie, aby poznać wyciek, gdy go zobaczysz. Również niewygodne jest umieszczanie logiki konstruktora po definicjach metod.
przeciw:
źródło
Każdy obiekt jest powiązany z obiektem prototypowym. Podczas próby uzyskania dostępu do właściwości, która nie istnieje, JavaScript przeszuka obiekt prototypowy obiektu pod kątem tej właściwości i zwróci ją, jeśli istnieje.
prototype
Właściwością konstruktora funkcji odnosi się do obiektu prototypu wszystkich przypadkach utworzone z tej funkcji przy użyciunew
.W pierwszym przykładzie dodajesz właściwość
x
do każdej instancji utworzonej za pomocąA
funkcji.W drugim przykładzie dodajesz właściwość do obiektu prototypowego, do którego wskazują wszystkie instancje
A
.Podsumowując, w pierwszym przykładzie do każdej instancji przypisana jest kopia funkcji . W drugim przykładzie pojedyncza kopia funkcji jest wspólna dla wszystkich instancji .
źródło
Co za różnica? => Dużo.
Myślę, że
this
wersja służy do enkapsulacji, czyli ukrywania danych. Pomaga manipulować zmiennymi prywatnymi.Spójrzmy na następujący przykład:
Teraz
prototype
strukturę można zastosować w następujący sposób:Różni dorośli mają różny wiek, ale wszyscy dorośli mają te same prawa.
Tak więc dodajemy go za pomocą prototypu, a nie tego.
Przyjrzyjmy się teraz implementacji.
Mam nadzieję że to pomoże.
źródło
Prototyp jest szablonem klasy; co dotyczy wszystkich jego przyszłych przypadków. Jest to szczególny przypadek obiektu.
źródło
Wiem, że na to odpowiedziano na śmierć, ale chciałbym pokazać rzeczywisty przykład różnic prędkości.
Tutaj tworzymy 2 miliony nowych obiektów za pomocą
print
metody w Chrome. Przechowujemy każdy obiekt w tablicy. Założenieprint
prototypu zajmuje około 1/2 czasu.źródło
Pozwól, że dam ci bardziej wyczerpującą odpowiedź, której nauczyłem się podczas szkolenia JavaScript.
Większość odpowiedzi wspomniała już o różnicy, tj. Kiedy prototypowanie funkcji jest wspólne dla wszystkich (przyszłych) instancji. Deklarowanie funkcji w klasie spowoduje utworzenie kopii dla każdej instancji.
Ogólnie rzecz biorąc, nie ma dobrego ani złego, to bardziej kwestia gustu lub decyzji projektowej w zależności od twoich wymagań. Prototyp jest jednak techniką rozwijaną w sposób obiektowy, jak mam nadzieję, zobaczycie na końcu tej odpowiedzi.
W swoim pytaniu pokazałeś dwa wzory. Spróbuję wyjaśnić jeszcze dwa i w razie potrzeby spróbuję wyjaśnić różnice. Możesz edytować / rozszerzać. We wszystkich przykładach chodzi o obiekt samochodowy, który ma lokalizację i może się poruszać.
Wzór dekoratora obiektów
Nie jestem pewien, czy ten wzór jest nadal aktualny, ale istnieje. I dobrze o tym wiedzieć. Po prostu przekazujesz obiekt i właściwość do funkcji dekoratora. Dekorator zwraca obiekt za pomocą właściwości i metody.
Klasy funkcjonalne
Funkcja w JavaScript jest specjalizowanym obiektem. Oprócz wywołania funkcja może przechowywać właściwości jak każdy inny obiekt.
W tym przypadku
Car
jest funkcja ( również myśl obiekt ), którą można wywołać tak jak zwykle. Ma właściwośćmethods
(która jest obiektem zmove
funkcją). PoCar
wywołaniu wywoływana jestextend
funkcja, która działa magicznie i rozszerzaCar
funkcję (myśl obiekt) o zdefiniowane metodymethods
.Ten przykład, choć inny, jest najbliższy pierwszemu przykładowi w pytaniu.
Klasy prototypowe
Pierwsze dwa wzorce umożliwiają dyskusję na temat stosowania technik do definiowania wspólnych metod lub stosowania metod, które są zdefiniowane bezpośrednio w ciele konstruktora. W obu przypadkach każda instancja ma swoją
move
funkcję.Wzorzec prototypowy nie nadaje się dobrze do tego samego badania, ponieważ współdzielenie funkcji za pośrednictwem delegacji prototypów jest właśnie celem wzorca prototypowego. Jak zauważyli inni, oczekuje się, że będzie mieć lepszą pamięć.
Jednak interesujący jest jeden punkt: każdy
prototype
obiekt ma właściwość wygodyconstructor
, która wskazuje na funkcję (obiekt myślowy), do której został dołączony.Odnośnie trzech ostatnich linii:
W tym przykładzie
Car
łączy się zprototype
obiektem, który łączy sięconstructor
zCar
samym sobą, tj.Car.prototype.constructor
JestCar
sobą. To pozwala dowiedzieć się, która funkcja konstruktora zbudowała określony obiekt.amy.constructor
Wyszukiwanie kończy się niepowodzeniem i dlatego jest delegowane doCar.prototype
, który ma właściwość konstruktora. I takamy.constructor
jestCar
.Ponadto
amy
jestinstanceof
Car
.instanceof
Operator działa, sprawdzając, czy prototyp obiektu prawy argument za (Car
) można znaleźć nigdzie w prototypie lewy argument jest (amy
) łańcucha.Na początku niektórzy programiści mogą się mylić. Zobacz poniższy przykład:
Te
instanceof
powroty operatorafalse
, ponieważDog
„s prototyp nie można znaleźć nigdzie wfido
” s łańcucha prototypów.fido
jest prostym obiektem, który jest tworzony za pomocą literału obiektu, tj. po prostu deleguje się doObject.prototype
.Wzory pseudoklasyczne
Jest to naprawdę kolejna forma wzorca prototypowego w formie uproszczonej i bardziej znana osobom, które programują na przykład w Javie, ponieważ używa ona
new
konstruktora.Robi to samo, co w przypadku wzoru prototypowego, jest to po prostu cukier syntaktyczny ponad wzorem prototypowym.
Jednak podstawowa różnica polega na tym, że w silnikach JavaScript są zaimplementowane optymalizacje, które mają zastosowanie tylko w przypadku stosowania wzorca pseudoklasycznego. Pomyśl o pseudoklasycznym wzorze prawdopodobnie szybszej wersji wzoru prototypowego; relacje między obiektami w obu przykładach są takie same.
Wreszcie nie powinno być zbyt trudne uświadomienie sobie, jak można realizować programowanie obiektowe. Istnieją dwie sekcje.
Jedna sekcja, która definiuje wspólne właściwości / metody w prototypie (łańcuch).
I kolejna sekcja, w której umieścisz definicje, które odróżniają obiekty od siebie (
loc
zmienna w przykładach).To pozwala nam stosować pojęcia takie jak nadklasa lub podklasa w JavaScript.
Dodaj lub edytuj. Po ukończeniu mógłbym zrobić z tego wiki społeczności.
źródło
Uważam, że @Matthew Crumley ma rację. Są funkcjonalnie , jeśli nie strukturalnie, równoważne. Jeśli użyjesz Firebug, aby spojrzeć na obiekty, które zostały utworzone za pomocą
new
, możesz zobaczyć, że są one takie same. Jednak moje preferencje byłyby następujące. Domyślam się, że to bardziej przypomina to, do czego jestem przyzwyczajony w C # / Java. To znaczy, zdefiniuj klasę, zdefiniuj pola, konstruktor i metody.EDYCJA Nie chciałem sugerować, że zakres zmiennej był prywatny, ja tylko próbowałem zilustrować, jak definiuję swoje klasy w javascript. Zmienna nazwa została zmieniona, aby to odzwierciedlić.
źródło
initialize
ix methods do not refer to the
_instance_var` wA
instancji, ale do globalnej. Użyj,this._instance_var
jeśli chcesz użyć_instance_var
właściwościA
instancji.Jak omówiono w innych odpowiedziach, jest to naprawdę kwestia wydajności, ponieważ funkcja w prototypie jest wspólna dla wszystkich instancji - zamiast funkcji tworzonej dla każdej instancji.
Złożyłem jsperf, aby to pokazać. Istnieje ogromna różnica w czasie potrzebnym do utworzenia instancji klasy, chociaż tak naprawdę jest to istotne tylko w przypadku tworzenia wielu instancji.
http://jsperf.com/functions-in-constructor-vs-prototype
źródło
Pomyśl o języku o typie statycznym, rzeczy
prototype
są statyczne, a rzeczythis
związane z instancjami.źródło