Staram się zrozumieć za kulisami JavaScript i trochę utknąłem w zrozumieniu tworzenia wbudowanych obiektów, zwłaszcza Obiektów i Funkcji oraz relacji między nimi.
Kiedy przeczytałem, że wszystkie wbudowane obiekty, takie jak Array, String itp., Są rozszerzeniem (dziedziczonym) z Object, założyłem, że Object jest pierwszym wbudowanym obiektem, który zostaje utworzony, a reszta obiektów dziedziczy po nim. Ale nie ma sensu, gdy się dowiesz, że Obiekty mogą być tworzone tylko przez funkcje, ale wtedy funkcje są również niczym innym jak obiektami Funkcji. Zaczęło to brzmieć jak dylemat kur i kurczaków.
Inną niezwykle mylącą rzeczą jest to, że jeśli console.log(Function.prototype)
drukuje funkcję, ale podczas drukowania console.log(Object.prototype)
drukuje obiekt. Dlaczego Function.prototype
funkcja, skoro miała być obiektem?
Ponadto, zgodnie z dokumentacją Mozilli, każdy javascript function
jest rozszerzeniem Function
obiektu, ale kiedy console.log(Function.prototype.constructor)
znów jest funkcją. Teraz jak możesz użyć czegoś, aby stworzyć to samemu (Mind = blown).
Ostatnią rzeczą Function.prototype
jest funkcja, ale mogę uzyskać dostęp do constructor
funkcji za pomocą Function.prototype.constructor
czy to znaczy, że Function.prototype
funkcja zwraca prototype
obiekt
źródło
Function.prototype
może być funkcją i mieć wewnętrzne pola. Więc nie, nie wykonujesz funkcji prototypowej podczas przeglądania jej struktury. Na koniec pamiętaj, że istnieje silnik interpretujący Javascript, więc obiekt i funkcja są prawdopodobnie tworzone w silniku, a nie z Javascript i specjalnych odnośników, takich jakFunction.prototype
iObject.prototype
mogą być po prostu interpretowane w specjalny sposób przez silnik.Odpowiedzi:
Jest to skomplikowane, łatwo jest go źle zrozumieć, a wiele początkujących książek o JavaScript robi to źle, więc nie ufaj wszystkim, co czytasz.
Byłem jednym z wdrażających silnik JS Microsoftu w latach 90. i w komitecie normalizacyjnym i popełniłem wiele błędów, łącząc tę odpowiedź. (Chociaż odkąd nie pracowałem nad tym od ponad 15 lat, być może wybaczę mi.) To trudne zadanie. Ale kiedy zrozumiesz dziedziczenie prototypów, wszystko będzie miało sens.
Zacznij od wyrzucenia wszystkiego, co wiesz o dziedziczeniu klasowym. JS używa dziedziczenia opartego na prototypach.
Następnie upewnij się, że masz bardzo jasną definicję tego, co oznacza „dziedziczenie”. Ludzie przyzwyczajeni do języków OO, takich jak C #, Java lub C ++, uważają, że dziedziczenie oznacza podtyp, ale dziedziczenie nie oznacza podtypu. Dziedziczenie oznacza, że członkowie jednej rzeczy są również członkami innej rzeczy . Nie musi to koniecznie oznaczać, że między tymi rzeczami istnieje poddziałanie! Tak wiele nieporozumień w teorii typów wynika z faktu, że ludzie nie zdają sobie sprawy z różnicy.
To jest po prostu nieprawda. Niektóre obiekty nie są tworzone przez wywołanie
new F
jakiejś funkcjiF
. Niektóre obiekty są tworzone przez środowisko wykonawcze JS zupełnie z niczego. Są jajka, których nie złożył żaden kurczak . Zostały one utworzone przez środowisko uruchomieniowe podczas jego uruchamiania.Powiedzmy, jakie są reguły i może to pomoże.
null
.prototype
obiektu zazwyczaj nie jest prototypem obiektu.prototype
członek obiektu funkcji F jest obiekt, który będzie prototyp obiektu utworzonego przeznew F()
.__proto__
członka, który naprawdę daje swój prototyp. (To jest już przestarzałe. Nie polegaj na tym.)prototype
którego są przypisywane podczas ich tworzenia.Function.prototype
.Podsumujmy.
Object
jestFunction.prototype
Object.prototype
jest obiektem prototypowym obiektu.Object.prototype
jestnull
Function
jestFunction.prototype
- to jedna z rzadkich sytuacji, w którychFunction.prototype
tak naprawdę jest prototypemFunction
!Function.prototype
jest obiektem prototypowym funkcji.Function.prototype
jestObject.prototype
Załóżmy, że tworzymy funkcję Foo.
Foo
jestFunction.prototype
.Foo.prototype
jest prototypowym obiektem Foo.Foo.prototype
jestObject.prototype
.Załóżmy, że mówimy
new Foo()
Foo.prototype
Upewnij się, że to ma sens. Narysujmy to. Owale są instancjami obiektowymi. Krawędzie
__proto__
oznaczają albo „prototyp”, alboprototype
„prototype
właściwość”.Środowisko wykonawcze musi tylko utworzyć wszystkie te obiekty i odpowiednio przypisać ich różne właściwości. Jestem pewien, że widzisz, jak by to było zrobione.
Teraz spójrzmy na przykład, który sprawdza twoją wiedzę.
Co to drukuje?
Co to
instanceof
znaczy?honda instanceof Car
oznacza „jestCar.prototype
równy dowolnemu obiektowi whonda
łańcuchu prototypów?”Tak to jest.
honda
Prototyp jestCar.prototype
, więc skończone. To drukuje się prawda.A co z drugim?
honda.constructor
nie istnieje, więc konsultujemy prototypCar.prototype
. KiedyCar.prototype
obiekt został utworzony, automatycznie otrzymał właściwośćconstructor
równąCar
, więc to prawda.A co z tym?
Co drukuje ten program?
Znów
lizard instanceof Reptile
oznacza „jestReptile.prototype
równy dowolnemu obiektowi wlizard
łańcuchu prototypów?”Tak to jest.
lizard
Prototyp jestReptile.prototype
, więc skończone. To drukuje się prawda.A teraz co
Możesz pomyśleć, że to również drukuje się prawdą, ponieważ
lizard
został skonstruowany z,new Reptile
ale byłbyś w błędzie. Rozumuj to.lizard
maconstructor
nieruchomość? Nie. Dlatego patrzymy na prototyp.lizard
jestReptile.prototype
, który jestAnimal
.Animal
maconstructor
nieruchomość? Nie. Więc patrzymy na jego prototyp.Animal
jestObject.prototype
iObject.prototype.constructor
jest tworzony przez środowisko wykonawcze i równyObject
.Reptile.prototype.constructor = Reptile;
W pewnym momencie powinniśmy byli to powiedzieć , ale nie pamiętaliśmy!Upewnij się, że wszystko ma dla ciebie sens. Narysuj kilka pól i strzałek, jeśli nadal są mylące.
Prototyp funkcji jest zdefiniowany jako funkcja, która po wywołaniu zwraca
undefined
. Wiemy już, żeFunction.prototype
toFunction
prototyp, co dziwne. DlategoFunction.prototype()
jest to legalne, a kiedy to zrobisz,undefined
wrócisz. To jest funkcja.Object
Prototyp nie posiada tę właściwość; to nie jest na żądanie. To tylko przedmiot.Function.prototype.constructor
jest po prostuFunction
, oczywiście. IFunction
jest funkcją.Nadmiernie o tym myślisz . Wszystko, co jest wymagane, to aby środowisko uruchomieniowe tworzyło kilka obiektów podczas uruchamiania. Obiekty to tylko tabele wyszukiwania, które łączą ciągi z obiektami. Gdy środowisko wykonawcze uruchamia się, wszystko to musi zrobić, to utworzyć kilka obiektów tuzin puste, a następnie rozpocząć przypisywanie
prototype
,__proto__
,constructor
, i tak dalej właściwości każdego obiektu, dopóki oni wykres że trzeba zrobić.Pomocne będzie, jeśli weźmiesz ten schemat, który ci dałem powyżej, i dodasz
constructor
do niego krawędzie. Szybko zobaczysz, że jest to bardzo prosty wykres obiektowy i że środowisko wykonawcze nie będzie miało problemu z jego utworzeniem.Dobrym ćwiczeniem byłoby zrobienie tego samemu. Tutaj zacznę. Użyjemy
my__proto__
w znaczeniu „obiekt prototypowy” imyprototype
„właściwości prototypowej”.I tak dalej. Czy możesz wypełnić resztę programu, aby zbudować zestaw obiektów o tej samej topologii, co „prawdziwe” wbudowane obiekty JavaScript? Jeśli to zrobisz, przekonasz się, że jest to niezwykle łatwe.
Obiekty w JavaScript to po prostu tabele odnośników, które łączą ciągi z innymi obiektami . Otóż to! Tu nie ma magii. Wiązujesz się w węzły, ponieważ wyobrażasz sobie ograniczenia, które tak naprawdę nie istnieją, tak że każdy obiekt musiał zostać stworzony przez konstruktora.
Funkcje to tylko obiekty, które mają dodatkową możliwość: być wywoływanym. Więc przejrzyj swój mały program symulacyjny i dodaj
.mycallable
właściwość do każdego obiektu, który wskazuje, czy można go wywołać, czy nie. To takie proste.źródło
__proto__
.__proto__
Prototypu obiektu jest zerowy.__proto__
Onew X()
toX.prototype
. Wszystkie obiekty funkcji mają prototyp funkcji, z__proto__
wyjątkiem samego prototypu funkcji.Object
aFunction
prototyp funkcji są funkcjami. Wszystkie te reguły są proste i określają topologię wykresu obiektów początkowych.Masz już wiele doskonałych odpowiedzi, ale chcę tylko krótko i jasno odpowiedzieć na twoją odpowiedź na temat tego, jak to wszystko działa, a ta odpowiedź to:
MAGIA!!!
Naprawdę, to wszystko.
Ludzie, którzy wdrażają silniki wykonanie ECMAScript muszą wdrożyć zasady ECMAScript, ale nie przestrzegania przez nich terminie ich realizacji.
Specyfikacja ECMAScript mówi, że A dziedziczy po B, ale B jest instancją A? Nie ma problemu! Najpierw utwórz A z prototypowym wskaźnikiem
NULL
, utwórz B jako instancję A, a następnie napraw prototypowy wskaźnik A, aby następnie wskazywał na B. Bułka z masłem.Mówisz, ale czekaj, nie ma sposobu, aby zmienić wskaźnik prototypu w ECMAScript! Ale o to chodzi: ten kod nie działa w silniku ECMAScript, ten kod to silnik ECMAScript. To ma mieć dostęp do wewnętrznych obiektów, które kod ECMAScript działa na silniku nie ma. W skrócie: może robić, co chce.
Nawiasem mówiąc, jeśli naprawdę chcesz, musisz to zrobić tylko raz: później możesz na przykład zrzucić pamięć wewnętrzną i załadować ten zrzut przy każdym uruchomieniu silnika ECMAScript.
Zauważ, że wszystko to nadal obowiązuje, nawet jeśli sam silnik ECMAScript został napisany w ECMAScript (tak jak na przykład w przypadku Narcyza Mozilli). Nawet wtedy kod ECMAScript, który implementuje silnik, nadal ma pełny dostęp do silnika, który implementuje , chociaż oczywiście nie ma dostępu do silnika, na którym działa .
źródło
Z specyfikacji ECMA 1
Nie rozumiem, jak to może być bardziej jasne !!!
</sarcasm>
Dalej widzimy:
Widzimy więc, że prototyp jest obiektem, ale niekoniecznie obiektem funkcji.
Mamy też ten interesujący titbit
http://www.ecma-international.org/ecma-262/8.0/index.html#sec-object-objects
i
źródło
sarcasm
przeciwnym razie ten pseudonim jest naprawdę nieprzejrzysty dla początkującego.Następujące typy obejmują każdą wartość w JavaScript:
boolean
number
undefined
(która obejmuje pojedynczą wartośćundefined
)string
symbol
(abstrakcyjne unikalne „rzeczy”, które są porównywane przez odniesienie)object
Każdy obiekt (tj. Wszystko) w JavaScript ma prototyp, który jest rodzajem obiektu.
Prototyp zawiera funkcje, które są również rodzajem obiektu 1 .
Obiekty mają również konstruktor, który jest funkcją, a zatem rodzajem obiektu.
Wszystko jest rekurencyjne, ale implementacja może to zrobić automatycznie, ponieważ w przeciwieństwie do kodu JavaScript, może tworzyć obiekty bez konieczności wywoływania funkcji JavaScript (ponieważ obiekty są tylko pamięcią kontrolowaną przez implementację).
Większość systemów obiektowych w wielu językach o typie dynamicznym jest okrągłych 2 w ten sposób. Na przykład w Pythonie klasy są obiektami, a klasa klas jest
type
, dlategotype
też jest instancją samego siebie.Najlepiej jest po prostu korzystać z narzędzi udostępnianych przez język i nie zastanawiać się nad tym, jak się tam dostali.
1 Funkcje są raczej szczególne, ponieważ można je wywoływać i są to jedyne wartości, które mogą zawierać nieprzezroczyste dane (ich ciało i być może zamknięcie).
2 To właściwie bardziej torturowana, rozgałęziona wstążka wygięta do tyłu nad sobą, ale „okrągły” jest wystarczająco blisko.
źródło