Czym __proto__ różni się od constructor.prototype?

163
function Gadget(name, color)
{
   this.name = name;
   this.color = color;
}

Gadget.prototype.rating = 3

var newtoy = new Gadget("webcam", "black")

newtoy.constructor.prototype.constructor.prototype.constructor.prototype 

Zawsze zwraca obiekt z ratingiem = 3.

Ale jeśli wykonam następujące czynności:

newtoy.__proto__.__proto__.__proto__

Łańcuch kończy się powrotem null.

Również w przeglądarce Internet Explorer, jak sprawdzić wartość null, jeśli nie ma __proto__właściwości?

xdevel2000
źródło
30
Ten diagram pomoże ci zrozumieć różnicę między prototypem a prototypem . Możesz podążać za łańcuchem proto z obiektu newtoy, a wtedy zrozumiesz, dlaczego 3. proto from newtoy jest zerowe.
bity
Również wynika to z diagramu, który newtoy.prototypenie jest równy newtoy.constructor.prototypei dlatego newtoy.constructor.prototypenie będzie miał właściwości o nazwie rating. Podobnie newtoy.constructor.prototype.constructor.propertyteż nie będzie miała właściwości o nazwie rating.
bity
Literówka w ostatnim komentarzu: w związku z tym newtoy.constructor.prototypebędzie miała właściwość o nazwie rating. Podobnie newtoy.constructor.prototype.constructor.propertybędzie mieć również właściwość o nazwie rating.
bity
1
@Royi Namir Wgrałem jsViz na github. Oto strona demonstracyjna . Proszę nie przejmować się tym, jak nieutwardzony (i brudny) jest sam kod. To bardzo stary projekt, którego nie tknąłem od zawsze.
bity

Odpowiedzi:

210

Ostatnio próbowałem to obejść i w końcu wymyśliłem tę „mapę”, która moim zdaniem rzuca pełne światło na tę sprawę

http://i.stack.imgur.com/KFzI3.png wprowadź opis obrazu tutaj

Wiem, że nie jestem pierwszą osobą, która to wymyśliła, ale ciekawsze było wymyślenie tego niż znalezienie :-). Zresztą po tym znalazłem np. Ten kolejny schemat, który chyba mówi w zasadzie to samo:

Układ obiektów JavaScript

Najbardziej zaskakujące było dla mnie odkrycie, że zamiast tego Object.__proto__wskazuje , ale jestem pewien, że jest ku temu dobry powód :-)Function.prototypeObject.prototype

Wklejam kod wymieniony na obrazku tutaj, jeśli ktoś chce go przetestować. Zwróć uwagę, że do obiektów dodawane są pewne właściwości, dzięki czemu można łatwo zorientować się, gdzie jesteśmy po wykonaniu pewnych skoków:

Object.O1='';
Object.prototype.Op1='';

Function.F1 = '';
Function.prototype.Fp1 = '';

Cat = function(){};
Cat.C1 = '';
Cat.prototype.Cp1 = '';

mycat = new Cat();
o = {};

// EDITED: using console.dir now instead of console.log
console.dir(mycat);
console.dir(o);
drodsou
źródło
2
@utsaina Very cool. Sprawdź inną graficzną reprezentację kodu opublikowanego przez OP. Myślę, że nasze diagramy są zgodne pod względem szczegółów technicznych.
bity
43
Powodem, dla którego Object.__proto__wskazuje, Function.prototypejest to, że Object()sama w sobie jest funkcją natywną, która tworzy instancję pustego obiektu. Dlatego Object()jest funkcją. Przekonasz się, że wskazują na to wszystkie inne główne __proto__właściwości typów natywnych Function.prototype. Object, Function, String, Number, I Arraywszyscy dziedziczą prototyp funkcji.
Obrót
@drodsou Twój drugi link jest niesamowity. Sprawdź to teraz, proszę;) mollypages.org/misc/js.mp Ładne wyjaśnienie: D
abhisekp
@Swivel "Dlatego Object () jest funkcją" - czy miałeś na myśli powiedzieć, że Object jest funkcją? bez ()
giorgim
2
@GiorgiMoniava Correct. Objectsama jest funkcją; wynik wykonania wywoływalnego Object(tj. zwrócenie wartości działania Object()) nie jest funkcją.
Obróć
67

constructorjest predefiniowaną właściwością [[DontEnum]] obiektu wskazywanego przez prototypewłaściwość obiektu funkcji i początkowo będzie wskazywać na sam obiekt funkcji.

__proto__ jest odpowiednikiem wewnętrznej właściwości [[Prototype]] obiektu, tj. jego rzeczywistego prototypu.

Kiedy tworzysz obiekt za pomocą newoperatora, jego wewnętrzna właściwość [[Prototype]] zostanie ustawiona na obiekt wskazywany przez właściwość funkcji konstruktora prototype.

Oznacza to, że .constructorzostanie oszacowana .__proto__.constructorfunkcja konstruktora użyta do utworzenia obiektu, a jak się dowiedzieliśmy, protoypewłaściwość tej funkcji została użyta do ustawienia [[Prototype]] obiektu.

Wynika z tego, że .constructor.prototype.constructorjest identyczny z .constructor(o ile te właściwości nie zostały nadpisane); zobacz tutaj, aby uzyskać bardziej szczegółowe wyjaśnienie.

Jeśli __proto__jest dostępny, możesz przejść przez rzeczywisty łańcuch prototypów obiektu. Nie ma sposobu, aby to zrobić w zwykłym ECMAScript3, ponieważ JavaScript nie został zaprojektowany do głębokich hierarchii dziedziczenia.

Christoph
źródło
3
To łącze „tutaj” jest złotym standardem. Przejdź tam, jeśli chcesz uzyskać pełny opis.
Ricalsin
Niezły chwyt z .constructor.prototypełańcuchem. Byłem też dla mnie niejasny, podczas gdy nie widziałem, że .constructorjest równe .__proto__.constructor. Co po prostu oznacza przechodzenie między funkcją konstruktora a jej prototypem.
Johnny_D
30

Dziedziczenie prototypów w JavaScript jest oparte na __proto__własności w tym sensie, że każdy obiekt dziedziczy zawartość obiektu, do którego odwołuje się jego __proto__własność.

prototypeNieruchomość jest specjalny tylko dla Functionobiektów i wyłącznie przy użyciu newoperatora nazwać Functionjako konstruktora. W tym przypadku utworzony obiekt __proto__zostanie ustawiony na obiekt konstruktora Function.prototype.

Oznacza to, że dodawanie do Function.prototypebędzie automatycznie odzwierciedlać wszystkie obiekty, do których __proto__odwołuje się Function.prototype.

Zastąpienie konstruktora Function.prototypeinnym obiektem nie zaktualizuje __proto__właściwości żadnego z już istniejących obiektów.

Należy pamiętać, że __proto__nie należy uzyskiwać bezpośredniego dostępu do właściwości , zamiast tego należy użyć Object.getPrototypeOf (object) .

Aby odpowiedzieć na pierwsze pytanie, stworzyłem niestandardowy diagram __proto__i prototypereferencje, niestety stackoverflow nie pozwala mi na dodanie obrazu z „mniej niż 10 reputacją”. Może innym razem.

[Edytuj] Rysunek używa [[Prototype]]zamiast, __proto__ponieważ tak specyfikacja ECMAScript odnosi się do obiektów wewnętrznych. Mam nadzieję, że wszystko zrozumiesz.

Oto kilka wskazówek, które pomogą Ci zrozumieć rysunek:

red    = JavaScript Function constructor and its prototype
violet = JavaScript Object constructor and its prototype
green  = user-created objects
         (first created using Object constructor or object literal {},
          second using user-defined constructor function)
blue   = user-defined function and its prototype
         (when you create a function, two objects are created in memory:
          the function and its prototype)

Zwróć uwagę, że constructorwłaściwość nie istnieje w utworzonych obiektach, ale jest dziedziczona z prototypu.

wprowadź opis obrazu tutaj

xorcus
źródło
@xorcus Czy możesz to wyjaśnić: new MyFunction()tworzy instancję obiektu, która __proto__powinna odwoływać się do swojego prototypu ctora, czyli MyFunction.prototype.Dlaczego MyFunction.prototype.__proto__odwołuje się do Object.prototype? powinien odnosić się (podobnie jak moja pierwsza próbka) do prototypu swojego twórcy, którym jest MyFunction.prototype(zauważcie, że MyFunction.prototypejest to instynkt Myfunction)
Royi Namir
@Royi Namir: MyFunction.prototype .__ proto__ odnosi się do Object.prototype, ponieważ MyFunction.prototype jest obiektem. Object.prototype jest dziedziczona przez wszystkie obiekty (zwykle na tym kończy się łańcuch dziedziczenia prototypów). Nie zgadzam się, że MyFunction.prototype jest instancją MyFunction. obj instanceof MyFunction <=> MyFunction.prototype.isPrototypeOf (obj) <=> MyFunction.prototype istnieje w łańcuchu prototypów obj. Tak nie jest w przypadku obiektu MyFunction.prototype
xorcus
14

Objectjest Ewa i Functionjest Adamem, Adam ( Function) używa swojej kości ( Function.prototype) do stworzenia Ewy ( Object). Więc kto stworzył Adama ( Function)? - Wynalazca języka JavaScript :-).

Zgodnie z odpowiedzią utsainy, chcę dodać więcej przydatnych informacji.

Najbardziej zaskakujące było dla mnie odkrycie, że zamiast tego Object.__proto__ wskazuje , ale jestem pewien, że jest ku temu dobry powód :-)Function.prototypeObject.prototype

Nie powinno być. Object.__proto__NIE powinien wskazywać Object.prototype. Zamiast tego wystąpienie Object o, o.__proto__powinno wskazywać na Object.prototype.

(Wybacz mi, że używam terminów classi instanceJavaScript, ale wiesz o tym :-)

Myślę, że Objectsama klasa jest przykładem Function, dlatego Object.__proto__ === Function.prototype. Dlatego: Objectjest Ewa i Functionjest Adamem, Adam ( Function) używa swojej kości ( Function.prototype), aby stworzyć Ewę ( Object).

Co więcej, nawet Functionsama klasa jest instancją Functionsamą w sobie, to znaczy Function.__proto__ === Function.prototype, także dlategoFunction === Function.constructor

Co więcej, zwykła klasa Catjest przykładem Function, to znaczy Cat.__proto__ === Function.prototype.

Powodem tego jest to, że kiedy tworzymy klasę w JavaScript, w rzeczywistości tworzymy funkcję, która powinna być instancją Function. Objecti Functionsą po prostu wyjątkowe, ale nadal są klasami, podczas gdy Catsą zwykłą klasą.

W związku z tym, w silniku JavaScript Google Chrome, następujące 4:

  • Function.prototype
  • Function.__proto__
  • Object.__proto__
  • Cat.__proto__

Wszystkie są ===(absolutnie równe) pozostałym trzem, a ich wartość wynosifunction Empty() {}

> Function.prototype
  function Empty() {}
> Function.__proto__
  function Empty() {}
> Object.__proto__
  function Empty() {}
> Cat.__proto__
  function Empty() {}
> Function.prototype === Function.__proto__
  true
> Function.__proto__ === Object.__proto__
  true
> Object.__proto__ === Cat.__proto__
  true

DOBRZE. Więc kto tworzy specjalny function Empty() {}( Function.prototype)? Pomyśl o tym :-)

Peter Lee
źródło
Zgadzam się z tym, z wyjątkiem ostatniej rzeczy: do czego function Empty() {}odnosisz się bycie równym Function.prototype itp.? Jaki jest kod, którego użyłeś w konsoli chrome?
drodsou
2
Poprawiłem ostatnią rzecz, którą wskazałeś. Ich wartość znajduje się function Empty() {}w Google Chrome. Dodałem również wyjście konsoli.
Peter Lee
wszystkie funkcje są instancjami Function, a zatem wszystkie funkcje dziedziczą ( _ _proto_ _) po Function.prototype. To takie proste :)
xorcus
Przepraszamy za komentowanie starego wątku. Ale czy są stworzone przez Inventor of Language?
Patel Parth
6

Naprawdę nie wiem, dlaczego ludzie nie poprawiali cię, gdzie jest rzeczywisty problem w twoim rozumieniu.

To znacznie ułatwiłoby wykrycie problemu

Zobaczmy więc, co się dzieje:

var newtoy = new Gadget("webcam", "black")

newtoy 
  .constructor //newtoy's constructor function is newtoy ( the function itself)
    .prototype // the function has a prototype property.( all functions has)
      .constructor // constructor here is a **property** (why ? becuase you just did `prototype.constructor`... see the dot ? )  ! it is not(!) the constructor function  !!! this is where your mess begins. it points back to the constructor function itself ( newtoy function)
         .prototype // so again we are at line 3 of this code snippet
            .constructor //same as line 4 ...
                .prototype 
                 rating = 3

Świetnie, więc teraz spójrzmy na to __proto__

Wcześniej pamiętaj o 2 rzeczach dotyczących __proto__ :

  1. Kiedy tworzysz obiekt za pomocą newoperatora, jego właściwość wewnętrzna [[Prototype]]/ proto__zostanie ustawiona na prototypewłaściwość (1) jego constructor functionlub „twórcy”, jeśli chcesz.

  2. Zakodowany na stałe w JS -: Object.prototype.__proto__jest null.

Nazwijmy te 2 punkty „ bill

newtoy
     .__proto__ // When `newtoy` was created , Js put __proto__'s value equal to the value of the cunstructor's prototype value. which is `Gadget.prototype`.
       .__proto__ // Ok so now our starting point is `Gadget.prototype`. so  regarding "bill" who is the constructor function now? watch out !! it's a simple object ! a regular object ! prototype is a regular object!! so who is the constructor function of that object ? Right , it's the `function Object(){...}`.  Ok .( continuing "bill" ) does it has a `prototype` property ? sure. all function has. it's `Object.prototype`. just remember that when Gadget.prototype was created , it's internal `__proto__` was refered to `Object.prototype` becuase as "bill" says :"..will be set to the `prototype` property of   its `constructor function`"
          .__proto__ // Ok so now our satrting point is `Object.prototype`. STOP. read bullet 2.Object.prototype.__proto__ is null by definition. when Object.prototype ( as an object) was created , they SET THE __PROTO__ AS NULL HARDCODED

Lepszy?

Royi Namir
źródło
2

Każda funkcja tworzy jej prototyp. A kiedy utworzymy obiekt za pomocą tego konstruktora funkcji, wówczas właściwość __proto__ mojego obiektu zacznie wskazywać na prototyp tej funkcji.

Apoorv Nag
źródło
1
Myślę, że chciałeś powiedzieć __proto__własność.
demisx
Tak. Miałem na myśli proto własność obiektu. Mam nadzieję, że informacje były przydatne.
Apoorv Nag
2

Jeśli wszystkie te liczby były przytłaczające, spójrzmy, co oznaczają te właściwości.

STH.prototype

Podczas tworzenia nowej funkcji równolegle tworzony jest pusty obiekt połączony z funkcją [[Prototype]]łańcuchem. Aby uzyskać dostęp do tego obiektu, używamy prototypewłaściwości funkcji.

function Gadget() {}
// in background, new object has been created
// we can access it with Gadget.prototype
// it looks somewhat like {constructor: Gadget}

Pamiętaj, że prototypewłaściwość jest dostępna tylko dla funkcji.

STH.constructor

Wspomniany powyżej obiekt prototypowy nie ma żadnych właściwości poza jedną - constructor. Ta właściwość reprezentuje funkcję, która utworzyła obiekt prototypowy.

var toy = new Gadget();

Tworząc Gadgetfunkcję, stworzyliśmy również obiekt podobny {constructor: Gadget}- to nic takiego Gadget.prototype. Jak constructorodnosi się do funkcji, która utworzyła prototyp obiektu, toy.constructorreprezentuje Gadgetfunkcję. Piszemy toy.constructor.prototypei {constructor: Gadget}znowu dostajemy .

Dlatego pojawia się błędne koło: możesz używać toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototypei zawsze będzie Gadget.prototype.

toy
.constructor    // Gadget
.prototype    // {constructor: Gadget}
.constructor    // Gadget
.prototype    // {constructor: Gadget}
// ...

STH .__ proto__

Chociaż prototypejest właściwością specyficzną dla funkcji, __proto__jest dostępna dla wszystkich obiektów, w jakiej się znajduje Object.prototype. Odnosi się do prototypu funkcji, która może stworzyć obiekt.

[].__proto__ === Array.prototype
// true

({}).__proto === Object.prototype
// true

Tutaj toy.__proto__jest Gadget.prototype. Tak Gadget.prototypejak obiekt ( {}) i obiekty są tworzone za pomocą Objectfunkcji (patrz przykład powyżej), otrzymujemy Object.prototype. To jest wyższy obiekt w JavaScript i __proto__może tylko wskazywać null.

toy
.__proto__    // Gadget.prototype (object looking like {constructor: Gadget})
.__proto__    // Object.prototype (topmost object in JS)
.__proto__    // null - Object.prototype is the end of any chain
Damian Czapiewski
źródło
0

Krótka odpowiedź: __proto__to odniesienie do prototypewłaściwości konstruktora, który utworzył obiekt.

Obiekty w JavaScript

Obiekt JavaScript jest typem wbudowanym dla zbioru zawierającego zero lub więcej właściwości. Właściwości to kontenery zawierające inne obiekty, wartości pierwotne lub funkcje.

Konstruktory w JavaScript

Funkcje są zwykłymi obiektami (które implementują się [[Call]]w terminach ECMA-262) z dodatkową możliwością wywoływania, ale odgrywają inną rolę w JavaScript: stają się konstruktorami ( fabrykami obiektów), jeśli zostaną wywołane przez newoperatora. Konstruktorzy są więc przybliżonym odpowiednikiem klas w innych językach.

Każda funkcja JavaScript jest w rzeczywistości instancją Functionwbudowanego obiektu funkcji, który ma specjalną właściwość o nazwie prototypeużywaną do implementacji dziedziczenia opartego na prototypach i właściwości wspólnych. Każdy obiekt utworzony przez funkcję konstruktora ma niejawne odniesienie (zwane prototypem lub __proto__) do wartości swojego konstruktora prototype.

Konstruktor prototypejest rodzajem planu budowania obiektów, ponieważ każdy obiekt utworzony przez konstruktora dziedziczy odniesienie do jego prototype.

Łańcuch prototypów

Obiekt określa swój prototyp za pomocą właściwości wewnętrznej [[Prototype]]lub __proto__. Relacja prototypów między dwoma obiektami dotyczy dziedziczenia: każdy obiekt może mieć inny obiekt jako swój prototyp. Prototyp może być nullwartością.

Łańcuch obiektów połączonych __proto__właściwością nazywany jest łańcuchem prototypów . Kiedy odwołuje się do właściwości obiektu, odnosi się ono do właściwości napotkanej w pierwszym obiekcie w łańcuchu prototypów, który zawiera właściwość o tej nazwie. Łańcuch prototypów zachowuje się tak, jakby był pojedynczym obiektem.

Zobacz ten obraz (pobrany z tego bloga ):

proto.jpg

Za każdym razem, gdy próbujesz uzyskać dostęp do właściwości w obiekcie, JavaScript rozpoczyna wyszukiwanie jej w tym obiekcie i kontynuuje pracę ze swoim prototypem, prototypem prototypu i tak dalej, aż do napotkania właściwości lub jeśli __proto__zachowa wartość null.

Ten typ dziedziczenia przy użyciu łańcucha prototypów jest często nazywany delegacją, aby uniknąć nieporozumień z innymi językami używającymi łańcucha klas.

Prawie wszystkie obiekty są instancjami Object, ponieważ Object.prototypesą ostatnim w łańcuchu prototypów. Ale Object.prototypenie jest przykładem, Objectponieważ Object.prototype.__proto__ma wartość null.

Możesz także stworzyć obiekt z nullprototypem takim jak ten:

var dict = Object.create(null);

Taki obiekt jest lepsze mapy (słownika) niż dosłownym obiektu, dlatego ten wzór jest czasami nazywany DICT wzór ( dict do słownika).

Uwaga: obiekty literalne utworzone za pomocą {}są instancjami, Objectponieważ ({}).__proto__jest odwołaniem do Object.prototype.

eigenslacker
źródło
Proszę podać źródło cytatów i artefaktów, których używasz. Zdjęcie wydaje się pochodzić ze strony giamir.com/pseudoclasses-and-prototypal-inheritance-in-JS , czy masz do niego prawa autorskie?
Bergi
@Bergi Podałem źródło obrazu. Większość cytatów, których użyłem, pochodzi ze standardu JS lub MDN
eigenslacker