Jak działa JavaScript .prototype?


2041

Nie przepadam za dynamicznymi językami programowania, ale napisałem sporo kodu JavaScript. Nigdy tak naprawdę nie zastanawiałem się nad tym programowaniem opartym na prototypach, czy ktoś wie, jak to działa?

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Pamiętam wiele dyskusji, które miałem z ludźmi jakiś czas temu (nie jestem do końca pewien, co robię), ale rozumiem, że nie ma pojęcia o klasie. To tylko przedmiot, a instancje tych obiektów są klonami oryginału, prawda?

Ale jaki jest dokładnie cel tej właściwości „.prototype” w JavaScript? Jaki to ma związek z tworzeniem obiektów?

Aktualizacja: poprawny sposób

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

Te slajdy naprawdę bardzo pomogły.


78
John Resig ma kilka slajdów na temat prototypów funkcji, które były dla mnie pomocne, gdy patrzyłem na ten temat (możesz również wprowadzić zmiany w kodzie i zobaczyć, co się stanie ...) http://ejohn.org/apps/learn/#64
John Foster

5
Świetny materiał referencyjny, dla celów informacyjnych tego pytania, być może umieść niektóre komentarze z witryny Johna na twojej odpowiedzi, na wypadek gdyby jego strona zmieniła się w taki sposób, że twój link nie jest już dostępny. Tak czy inaczej +1, pomogło mi.
Chris

95
+1 za link do slajdu Ninja JavaScript Johna Resiga # 64 . Począwszy od tamtej pory było to bardzo pomocne i wydaje mi się, że poprawnie rozumiem prototypy.
płatny frajer

4
Czy naprawdę potrzebujemy funkcjonalnego obiektu do zastosowania prototypu? jeśli tak niż dlaczego?
Anshul,

Odpowiedzi:


1007

Każdy obiekt JavaScript posiada wewnętrzny „gniazdo” o nazwie [[Prototype]], której wartość jest albo nullalbo object. Możesz myśleć o gnieździe jako o właściwości obiektu, wewnętrznej w silniku JavaScript, ukrytej przed pisanym kodem. Kwadratowe nawiasy kwadratowe [[Prototype]]są celowe i stanowią konwencję specyfikacji ECMAScript oznaczającą gniazda wewnętrzne.

Wartość wskazywana przez [[Prototype]]obiekt jest potocznie nazywana „prototypem tego obiektu”.

Jeśli uzyskujesz dostęp do właściwości za pomocą notacji dot ( obj.propName) lub nawiasu klamrowego ( obj['propName']), a obiekt nie ma bezpośrednio takiej właściwości (tj. Własność własna , którą można sprawdzić przez obj.hasOwnProperty('propName')), środowisko wykonawcze szuka właściwości o tej nazwie w obiekcie, do którego się odwołuje przez [[Prototype]]zamiast. Jeśli [[Prototype]] również nie ma takiej właściwości, [[Prototype]]jest ona kolejno sprawdzana i tak dalej. W ten sposób prototypowy łańcuch oryginalnego obiektu przechodzi do momentu znalezienia dopasowania lub osiągnięcia jego końca. Na szczycie łańcucha prototypów znajduje się nullwartość.

Nowoczesne implementacje JavaScript umożliwiają dostęp do odczytu i / lub zapisu [[Prototype]]w następujący sposób:

  1. Do newnapędu (konfiguruje łańcucha prototyp na domyślnej obiektu zwróconej od funkcji konstruktora)
  2. Słowo extendskluczowe (konfiguruje łańcuch prototypów podczas korzystania ze składni klasy),
  3. Object.createustawi dostarczony argument jako [[Prototype]]wynikowy obiekt,
  4. Object.getPrototypeOfi Object.setPrototypeOf(pobierz / ustaw tworzenie obiektu [[Prototype]] po ) i
  5. Standaryzowana właściwość akcesor (tj. Getter / setter) o nazwie __proto__(podobna do 4.)

Object.getPrototypeOfi Object.setPrototypeOfsą preferowane __proto__, po części dlatego, że zachowanie o.__proto__ jest niezwykłe, gdy obiekt ma prototyp null.

Obiekt [[Prototype]]jest początkowo ustawiany podczas tworzenia obiektu.

Jeśli utworzysz nowy obiekt za pomocą new Func(), obiekt [[Prototype]]zostanie domyślnie ustawiony na obiekt, do którego odnosi się Func.prototype.

Należy pamiętać, że dlatego wszystkie klasy i wszystkie funkcje, których można używać z newoperatorem, mają własność nazwaną .prototypeoprócz własnego [[Prototype]]gniazda wewnętrznego. To podwójne użycie słowa „prototyp” jest źródłem niekończących się nieporozumień wśród nowych użytkowników języka.

Korzystanie newz funkcji konstruktora pozwala nam symulować klasyczne dziedziczenie w JavaScript; chociaż system dziedziczenia JavaScript jest - jak widzieliśmy - prototypowy, a nie oparty na klasach.

Przed wprowadzeniem składni klas do JavaScript funkcje konstruktora były jedynym sposobem symulacji klas. Możemy myśleć o właściwościach obiektu, do którego odwołuje się właściwość funkcji konstruktora, .prototypejako wspólnych elementach; to znaczy. członkowie, którzy są tacy sami dla każdej instancji. W systemach opartych na klasach metody są implementowane w taki sam sposób dla każdej instancji, więc metody są dodawane koncepcyjnie do .prototypewłaściwości; pola obiektu są jednak specyficzne dla instancji i dlatego są dodawane do samego obiektu podczas budowy.

Bez składni klas programiści musieli ręcznie skonfigurować łańcuch prototypów, aby osiągnąć funkcjonalność zbliżoną do klasycznego dziedziczenia. Doprowadziło to do przewagi różnych sposobów osiągnięcia tego celu.

Oto jeden ze sposobów:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... a oto inny sposób:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

Składnia klas wprowadzona w ES2015 upraszcza rzeczy, zapewniając extendsjako „jedyny prawdziwy sposób” konfiguracji łańcucha prototypów w celu symulacji klasycznego dziedziczenia w JavaScript.

Podobnie, jak w powyższym kodzie, jeśli użyjesz składni klasy, aby utworzyć nowy obiekt w następujący sposób:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... wynikowy obiekt [[Prototype]]zostanie ustawiony na instancję Parent, której [[Prototype]]z kolei jest Parent.prototype.

Wreszcie, jeśli utworzysz nowy obiekt za pomocą Object.create(foo), wynikowy obiekt [[Prototype]]zostanie ustawiony na foo.


1
Więc robię coś złego, definiując nowe właściwości właściwości prototypu w moim krótkim fragmencie?
John Leidegren,

3
Myślę, że to właśnie oznacza posiadanie obiektów funkcyjnych jako obywateli pierwszej klasy.
John Leidegren,

8
Nienawidzę niestandardowych rzeczy, zwłaszcza w językach programowania, dlaczego istnieje nawet proto, skoro wyraźnie nie jest potrzebne?
John Leidegren,

1
@John __proto__ jest potrzebny tylko do użytku wewnętrznego przez interpretera JS. Każdy obiekt musi wiedzieć, jakie właściwości i metody są częścią prototypu, a które są częścią samego obiektu. Interpreter JS musi mieć możliwość wywoływania prototypowych metod na obiekcie.
BMiner

17
zauważ, że użycie [[Prototyp]] jest celowe - ECMA-262 dołącza nazwy właściwości wewnętrznych do podwójnych nawiasów kwadratowych
Christoph

1798

W języku implementującym klasyczne dziedziczenie, takim jak Java, C # lub C ++, zaczynasz od utworzenia klasy - schematu dla swoich obiektów - a następnie możesz utworzyć nowe obiekty z tej klasy lub możesz rozszerzyć klasę, definiując nową klasę, która jest rozszerzana oryginalna klasa.

W JavaScript najpierw tworzysz obiekt (nie ma pojęcia klasy), a następnie możesz rozszerzać własny obiekt lub tworzyć z niego nowe obiekty. Nie jest to trudne, ale trochę obce i trudne do metabolizmu dla kogoś przyzwyczajonego do klasycznego sposobu.

Przykład:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

Do tej pory rozszerzałem podstawowy obiekt, teraz tworzę inny obiekt, a następnie dziedziczę po Person.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Chociaż, jak powiedziano, nie mogę wywołać setAmountDue (), getAmountDue () na Osobie.

//The following statement generates an error.
john.setAmountDue(1000);

352
Myślę, że odpowiedzi na stackoverflow są interesujące nie tylko dla oryginalnego plakatu, ale także dla dużej społeczności innych osób czających się lub pochodzących z wyszukiwań. Byłem jednym z nich i korzystałem ze starych postów. Myślę, że mógłbym przyczynić się do innych odpowiedzi, dodając kilka przykładów kodu. O twoim pytaniu: jeśli pominiesz nowy, to nie zadziała. kiedy wywołam myCustomer.sayMyName (), zwraca „myCustomer.sayMyName nie jest funkcją”. Najłatwiej jest eksperymentować z firebugiem i zobaczyć, co się stanie.
stivlo

7
O ile rozumiem var Person = function (name) {...}; definiuje funkcję konstruktora zdolną do budowania Obiektów Person. Więc nie ma jeszcze obiektu, tylko funkcja anonimowego konstruktora jest przypisana do osoby. To bardzo dobre wytłumaczenie: helephant.com/2008/08/how-javascript-objects-work
stivlo

17
OSTRZEŻENIE: Ta odpowiedź pomija fakt, że konstruktor klasy nadrzędnej nie jest wywoływany dla poszczególnych instancji. Jedynym powodem, dla którego działa, jest to, że zrobił dokładnie to samo (ustawiając nazwę) zarówno w podrzędnym, jak i nadrzędnym konstruktorze. Aby uzyskać bardziej szczegółowe wyjaśnienie typowych błędów popełnianych podczas próby dziedziczenia w JavaScript (i ostateczne rozwiązanie), zobacz: ten przepełnienie stosu
Aaren Cordova

3
Zauważam, że ta odpowiedź również nie wspomina, że ​​używając „new Person ()” jako prototypu, tak naprawdę ustawiasz właściwość wystąpienia „name” „Person” na statyczną właściwość „Customer” (więc wszyscy Klienci instancje będą miały tę samą właściwość). Chociaż jest to dobry podstawowy przykład, NIE Rób tego. :) Utwórz nową anonimową funkcję, która będzie działać jako „most”, ustawiając jej prototyp na „Person.prototype”, a następnie utwórz z niej instancję i zamiast tego ustaw „Customer.prototype” na tę anonimową instancję.
James Wilkins

10
W odniesieniu do Customer.prototype = new Person();linii MDN pokazuje przykład, używając Customer.prototype = Object.create(Person.prototype)i stwierdza, że „częstym błędem jest tutaj użycie„ new Person () ”” . źródło
Rafael Eyng

186

Jest to bardzo prosty prototypowy model obiektowy, który podczas objaśnienia byłby traktowany jako próbka, bez komentarza:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

Jest kilka kluczowych punktów, które musimy wziąć pod uwagę przed omówieniem koncepcji prototypu.

1- Jak faktycznie działają funkcje JavaScript:

Aby zrobić pierwszy krok, musimy dowiedzieć się, jak faktycznie działają funkcje JavaScript, jako klasa taka jak funkcja wykorzystująca thissłowo kluczowe lub po prostu jako zwykła funkcja z argumentami, co robi i co zwraca.

Powiedzmy, że chcemy stworzyć Personmodel obiektowy. ale w tym kroku będę próbował zrobić dokładnie to samo bez użycia słowa kluczowego prototypeinew .

Więc na tym etapie functions, objectsa thissłowa kluczowego, to wszystko, co mamy.

Pierwszym pytaniem byłoby, w jaki sposób thissłowo kluczowe może być przydatne bez użycia newsłowa kluczowego .

Aby odpowiedzieć, powiedzmy, że mamy pusty obiekt i dwie funkcje, takie jak:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

a teraz bez użycia newsłowa kluczowego, w jaki sposób moglibyśmy korzystać z tych funkcji. JavaScript ma więc 3 różne sposoby:

za. pierwszym sposobem jest wywołanie funkcji jako zwykłej funkcji:

Person("George");
getName();//would print the "George" in the console

w tym przypadku byłby to bieżący obiekt kontekstu, którym zwykle jest windowobiekt globalny w przeglądarce lub GLOBALw Node.js. Oznacza to, że mielibyśmy, window.name w przeglądarce lub GLOBAL.name w Node.js, z „George” jako jego wartością.

b. Możemy dołączyć je do obiektu jako jego właściwości

- Najłatwiej to zrobić, modyfikując pusteperson obiekt, na przykład:

person.Person = Person;
person.getName = getName;

w ten sposób możemy je nazwać:

person.Person("George");
person.getName();// -->"George"

a teraz personobiekt jest jak:

Object {Person: function, getName: function, name: "George"}

- Innym sposobem dołączenia właściwości do obiektu jest użycie prototypetego obiektu, który można znaleźć w dowolnym obiekcie JavaScript o nazwie __proto__, i próbowałem wyjaśnić to trochę w części podsumowującej. Możemy więc uzyskać podobny wynik, wykonując:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

Ale w ten sposób faktycznie modyfikujemy Object.prototype, ponieważ za każdym razem, gdy tworzymy obiekt JavaScript za pomocą literals ( { ... }), jest on tworzony na podstawie Object.prototype, co oznacza, że ​​zostaje on dołączony do nowo utworzonego obiektu jako atrybut o nazwie__proto__ , więc jeśli go zmienimy , podobnie jak w przypadku naszego poprzedniego fragmentu kodu, wszystkie obiekty JavaScript zostałyby zmienione, co nie jest dobrą praktyką. Więc jaka może być teraz lepsza praktyka:

person.__proto__ = {
    Person: Person,
    getName: getName
};

a teraz inne przedmioty są spokojne, ale nadal nie wydaje się to dobrą praktyką. Mamy więc jeszcze jedno rozwiązanie, ale aby skorzystać z tego rozwiązania, powinniśmy wrócić do tego wiersza kodu, w którym personutworzono obiekt (var person = {}; ), a następnie zmienić go w następujący sposób:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

tworzy nowy JavaScript Objecti dołącza go propertiesObjectdo pliku__proto__ atrybutu. Aby upewnić się, że możesz:

console.log(person.__proto__===propertiesObject); //true

Problem w tym, że masz dostęp do wszystkich właściwości zdefiniowanych __proto__na pierwszym poziomieperson obiektu (więcej szczegółów znajdziesz w części podsumowującej).


jak widzisz, używając dowolnego z tych dwóch sposobów this wskazywałoby dokładnie na personobiekt.

do. JavaScript ma inny sposób na zapewnienie funkcji this, która polega na wywołaniu lub zastosowaniu do wywołania funkcji.

Metoda apply () wywołuje funkcję o podanej wartości i argumentach podanych jako tablica (lub obiekt podobny do tablicy).

i

Metoda call () wywołuje funkcję o podanej wartości i argumentach podanych indywidualnie.

w ten sposób, który jest moim ulubionym, możemy łatwo wywoływać nasze funkcje, takie jak:

Person.call(person, "George");

lub

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

te 3 metody są ważnymi początkowymi krokami do zrozumienia funkcjonalności .prototypu.


2- Jak działa new słowo kluczowe?

to jest drugi krok do zrozumienia .prototypefunkcjonalności. oto, czego używam do symulacji procesu:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

w tej części postaram się wykonać wszystkie kroki, które wykonuje JavaScript, bez użycia newsłowa kluczowego i prototype, gdy używasz newsłowa kluczowego. więc kiedy to robimy new Person("George"), Personfunkcja służy jako konstruktor. Oto, co robi JavaScript, jeden po drugim:

za. po pierwsze tworzy pusty obiekt, w zasadzie pusty skrót, taki jak:

var newObject = {};

b. następnym krokiem JavaScript jest dołączenie wszystkich prototypowych obiektów do nowo utworzonego obiektu

mamy my_person_prototypetutaj podobny do obiektu prototypowego.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

To nie jest sposób, w jaki JavaScript faktycznie dołącza właściwości zdefiniowane w prototypie. Rzeczywisty sposób związany jest z koncepcją łańcucha prototypów.


za. & b. Zamiast tych dwóch kroków możesz uzyskać dokładnie taki sam wynik, wykonując:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

teraz możemy wywołać getNamefunkcję w naszym my_person_prototype:

newObject.getName();

do. następnie przekazuje ten obiekt do konstruktora,

możemy to zrobić za pomocą naszej próbki, takiej jak:

Person.call(newObject, "George");

lub

Person.apply(newObject, ["George"]);

wtedy konstruktor może zrobić, co chce, ponieważ to wewnątrz tego konstruktora jest obiekt, który został właśnie utworzony.

teraz wynik końcowy przed symulacją innych kroków: Object {name: "George"}


Podsumowanie:

Zasadniczo, gdy używasz nowego słowa kluczowego w funkcji, wywołujesz tę funkcję, a ta funkcja służy jako konstruktor, więc mówiąc:

new FunctionName()

JavaScript wewnętrznie tworzy obiekt, pusty skrót, a następnie przekazuje ten obiekt do konstruktora, a następnie konstruktor może zrobić co chce, ponieważ to wewnątrz tego konstruktora jest właśnie utworzonym obiektem, a następnie daje ten obiekt oczywiście jeśli nie użyłeś instrukcji return w swojej funkcji lub jeśli umieściłeś return undefined;na końcu treści funkcji.

Kiedy JavaScript zaczyna szukać właściwości na obiekcie, pierwszą rzeczą, którą robi, jest wyszukiwanie na tym obiekcie. A potem jest tajna właściwość, [[prototype]]którą zwykle mamy, __proto__a ta właściwość właśnie patrzy na JavaScript. A kiedy przegląda __proto__, o ile jest to kolejny obiekt JavaScript, ma swój własny __proto__atrybut, idzie w górę i w górę, aż dojdzie do punktu, w którym następny __proto__jest pusty. Punkt jest jedynym obiektem w JavaScript, którego __proto__atrybut ma wartość null, to Object.prototypeobiekt:

console.log(Object.prototype.__proto__===null);//true

i tak działa dziedziczenie w JavaScript.

Prototypowy łańcuch

Innymi słowy, gdy masz właściwość prototypową funkcji i wywołujesz nową funkcję, po zakończeniu JavaScript w poszukiwaniu tego nowo utworzonego obiektu pod kątem właściwości, przejdzie on do funkcji .prototypei możliwe jest, że ten obiekt ma swoją własny wewnętrzny prototyp. i tak dalej.


6
a) Proszę nie wyjaśniać prototypów, kopiując właściwości b) Ustawienie wewnętrznego [[prototypu]] dzieje się przed zastosowaniem funkcji konstruktora w instancji, proszę zmienić kolejność c) jQuery jest całkowicie nie na temat w tym pytaniu
Bergi

1
@Bergi: dziękuję za zwrócenie uwagi, byłbym wdzięczny, jeśli dasz mi znać, czy teraz jest w porządku.
Mehran Hatami

7
Czy możesz to ułatwić? Masz rację we wszystkich kwestiach, ale uczniowie, którzy czytają to wyjaśnienie, mogą być naprawdę zdezorientowani po raz pierwszy. wybierz prostszy przykład i pozwól kodowi się wyjaśnić lub dodaj kilka komentarzy, aby wyjaśnić, co masz na myśli.
PM

2
@PM: Dziękujemy za opinię. Starałem się, aby było to tak proste, jak to możliwe, ale myślę, że masz rację, to wciąż ma pewne niejasne punkty. Spróbuję go zmodyfikować, a także będę bardziej opisowy. :)
Mehran Hatami

1
@AndreaMattioli, ponieważ w ten sposób tworzysz zupełnie nowy obiekt i zastępujesz stary, który może być również współużytkowany przez inne obiekty. Zastępując __proto__najpierw usuniesz wszystkie właściwości prototypu najwyższego poziomu, a następnie będziesz mieć świeżą bazę proto, która nie będzie już udostępniana, chyba że ją udostępnisz.
Mehran Hatami

77

Siedem prototypów Koanów

Gdy Ciro San zszedł po Mount Fire Fox po głębokiej medytacji, jego umysł był czysty i spokojny.

Jego ręka była jednak niespokojna i sama chwyciła pędzel i zanotowała następujące nuty.


0) Dwie różne rzeczy można nazwać „prototypem”:

  • własność prototypu, jak w obj.prototype

  • prototypowa własność wewnętrzna, oznaczona jak [[Prototype]] w ES5 .

    Można go odzyskać za pomocą ES5 Object.getPrototypeOf().

    Firefox udostępnia go za pośrednictwem __proto__właściwości jako rozszerzenie. ES6 wspomina teraz o niektórych opcjonalnych wymaganiach dotyczących __proto__.


1) Te pojęcia istnieją, aby odpowiedzieć na pytanie:

Kiedy to zrobię obj.property, gdzie JS szuka .property?

Intuicyjnie klasyczne dziedziczenie powinno wpływać na wyszukiwanie właściwości.


2)

  • __proto__służy do .wyszukiwania właściwości kropki jak w obj.property.
  • .prototypenie jest używany do wyszukiwania bezpośrednio, tylko pośrednio, ponieważ określa __proto__przy tworzeniu obiektu za pomocą new.

Kolejność wyszukiwania to:

  • objwłaściwości dodane przy pomocy obj.p = ...lubObject.defineProperty(obj, ...)
  • właściwości obj.__proto__
  • właściwości obj.__proto__.__proto__ itd
  • jeśli jakieś __proto__null, wróć undefined.

Jest to tak zwany łańcuch prototypowy .

Możesz uniknąć .wyszukiwania za pomocą obj.hasOwnProperty('key')iObject.getOwnPropertyNames(f)


3) Istnieją dwa główne sposoby ustawienia obj.__proto__:

  • new:

    var F = function() {}
    var f = new F()

    następnie newustawił:

    f.__proto__ === F.prototype

    To jest miejsce, gdzie .prototypewykorzystywane wystąpią.

  • Object.create:

     f = Object.create(proto)

    zestawy:

    f.__proto__ === proto

4) Kod:

var F = function(i) { this.i = i }
var f = new F(1)

Odpowiada poniższemu diagramowi (niektóre Numberrzeczy zostały pominięte):

(Function)       (  F  )                                      (f)----->(1)
 |  ^             | | ^                                        |   i    |
 |  |             | | |                                        |        |
 |  |             | | +-------------------------+              |        |
 |  |constructor  | |                           |              |        |
 |  |             | +--------------+            |              |        |
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |  |             |                | +----------+              |        |
 |  |             |                | |                         |        |
 |  |             |                | | +-----------------------+        |
 |  |             |                | | |                                |
 v  |             v                v | v                                |
(Function.prototype)              (F.prototype)                         |
 |                                 |                                    |
 |                                 |                                    |
 |[[Prototype]]                    |[[Prototype]]          [[Prototype]]|
 |                                 |                                    |
 |                                 |                                    |
 | +-------------------------------+                                    |
 | |                                                                    |
 v v                                                                    v
(Object.prototype)                                       (Number.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

Ten schemat pokazuje wiele predefiniowanych węzłów obiektowych:

  • null
  • Object
  • Object.prototype
  • Function
  • Function.prototype
  • 1
  • Number.prototype(można znaleźć za pomocą (1).__proto__nawiasu obowiązkowego, aby spełnić składnię)

Nasze 2 wiersze kodu utworzyły tylko następujące nowe obiekty:

  • f
  • F
  • F.prototype

ijest teraz własnością, fponieważ kiedy to zrobisz:

var f = new F(1)

oblicza Fsię thisjako wartość, która newzostanie zwrócona, a następnie przypisana f.


5) .constructor zazwyczaj pochodzi F.prototypeprzez .wyszukiwarkę:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

Kiedy piszemy f.constructor, JavaScript .sprawdza, jak:

  • f nie ma .constructor
  • f.__proto__ === F.prototypema .constructor === F, więc weź to

Wynik f.constructor == Fjest intuicyjnie poprawny, ponieważ Fsłuży do konstruowania f, np. Ustawiania pól, podobnie jak w klasycznych językach OOP.


6) Klasyczną składnię dziedziczenia można osiągnąć, manipulując łańcuchami prototypów.

ES6 dodaje słowa kluczowe classi extends, które w większości są cukrem składniowym dla wcześniej możliwego szaleństwa manipulacji prototypem.

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
c = new C(1)
c.inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

Uproszczony schemat bez wszystkich predefiniowanych obiektów:

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

Zastanówmy się, jak działają następujące elementy:

c = new C(1)
c.inc() === 2

Pierwszy wiersz ustawiony c.ijest 1zgodnie z opisem w „4)”.

W drugiej linii, kiedy robimy:

c.inc()
  • .incznajduje się w [[Prototype]]łańcuchu: c-> C-> C.prototype->inc
  • kiedy wywołujemy funkcję w Javascript as X.Y(), JavaScript automatycznie ustawia się thisna wartość równą Xw Y()wywołaniu funkcji!

Ta sama logika wyjaśnia również d.inci d.inc2.

W tym artykule https://javascript.info/class#not-just-a-syntax-sugar wspomina o dalszych efektach, o których classwarto wiedzieć. Niektóre z nich mogą nie być osiągalne bez classsłowa kluczowego (sprawdź, które TODO):


1
@tomasb dzięki! „Nie wiem, skąd to masz”: po tym, jak zobaczyłem kilka tych dynamicznych języków, zauważyłem, że najważniejsze w ich systemie klas jest to, jak .działa wyszukiwanie (i ile kopii danych jest zrobionych) . Więc postanowiłem zrozumieć ten punkt. Reszta to Google + posty na blogu + tłumacz Js pod ręką. :)
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

1
Nadal nie rozumiem, dlaczego g.constructor === Object, ponieważ powiedziałeś, że „4) Kiedy wykonasz f = nowy F, nowy ustawia również f.constructor = F”. Czy mógłbyś mi wyjaśnić więcej? W każdym razie to najlepsza odpowiedź, jakiej szukam. Dziękuję bardzo!
nguyenngoc101,

@ nguyenngoc101 dzięki! sets f.constructor = FCzęść była rażąco błędne i sprzeczne z dalszych sekcjach: .constructorznaleziono poprzez .odnośnika na łańcuchu prototypów. Naprawiono to teraz.
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

z całej dyskusji, co otrzymuję (pochodzi z klasycznego dziedziczenia), jeśli utworzę funkcję konstruktora i spróbuję utworzyć jej instancję za pomocą nowego operatora, otrzymam tylko metody i właściwości, które zostały dołączone do obiektu proto, dlatego konieczne jest dołączenie całej metody i właściwości do proto obiektu, jeśli chcemy dziedziczyć, prawda?
blackHawk

1
@CiroSantilli 刘晓波 死 六四 事件 法轮功 Nie sądzę, że to błąd w Chromium. Myślę, że to tylko symptom, od którego fprototyp jest ustawiony Fdopiero na etapie budowy; fnie będzie o tym wiedział ani się nim przejmować F.prototypew żadnym momencie po jego pierwszym zbudowaniu.
John Glassmyer,

76

prototypepozwala tworzyć zajęcia. jeśli nie używasz prototype, staje się statyczny.

Oto krótki przykład.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

W powyższym przypadku masz test wywołania funkcji statycznej. Dostęp do tej funkcji można uzyskać tylko poprzez obj.test, w którym można sobie wyobrazić, że jest klasą.

gdzie jak w poniższym kodzie

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Obiekt stał się klasą, którą można teraz tworzyć. Może istnieć wiele instancji obj i wszystkie majątest funkcję.

Powyżej jest moje zrozumienie. Robię z niej wiki społeczności, aby ludzie mogli mnie poprawić, jeśli się mylę.


13
-1: prototypejest własnością funkcji konstruktora, a nie instancji, tzn. Twój kod jest nieprawidłowy! Być może miałeś na myśli niestandardową właściwość __proto__przedmiotów, ale to zupełnie inna bestia ...
Christoph

@Christoph - Dziękujemy za zwrócenie na to uwagi. Zaktualizowałem przykładowy kod.
Ramesh

3
Jest o wiele więcej ... Plus JavaScript nie jest językiem klasowym - zajmuje się dziedziczeniem za pośrednictwem prototypów, musisz omówić różnice bardziej szczegółowo!
James

5
Myślę, że ta odpowiedź jest trochę myląca.
Armin Cifuentes,

Być może odpowiedź jest „myląca”, ale wyjaśnia, do czego służy prototyp, a teraz wszystko jest dla mnie jasne, po tych wszystkich „odpowiedziach” z setkami głosów „w górę”. Dziękuję Ci.
Aleks

66

Po przeczytaniu tego wątku czuję się pomylony z łańcuchem prototypów JavaScript, a potem znalazłem te wykresy

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance * [[protytype]] * i właściwość <code> prototype </code> obiektów funkcji

jest to przejrzysty wykres pokazujący dziedziczenie JavaScript według Prototype Chain

i

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

ten zawiera przykład z kodem i kilkoma ładnymi schematami.

łańcuch prototypów ostatecznie wraca do Object.prototype.

łańcuch prototypów może być technicznie przedłużany tak długo, jak chcesz, za każdym razem poprzez ustawienie prototypu podklasy równej obiektowi klasy nadrzędnej.

Mam nadzieję, że zrozumienie łańcucha prototypów JavaScript jest również pomocne.


Czy możliwe jest wielokrotne dziedziczenie w Javascript?

Czy Foo jest tutaj dosłownie obiektem czy obiektem funkcji? Jeśli jest to dosłowny obiekt, uważam, że Foo.prototype nie będzie wskazywał Foo za pośrednictwem konstruktora.
Madhur Ahuja

@ user3376708 JavaScript obsługuje tylko pojedyncze dziedziczenie ( źródło )
Rafael Eyng 1'14

@ Nuno_147 Na początku nie jest jasne, ale jeśli spojrzysz wystarczająco długo, możesz coś z tego wyciągnąć.
marcelocra

3
Czy możesz wyjaśnić, co [[Prototype]]to znaczy?
CodyBugstein

40

Każdy obiekt ma wewnętrzną właściwość [[Prototyp]] , łącząc go z innym obiektem:

object [[Prototype]]  anotherObject

W tradycyjnym javascript połączony obiekt jest prototypewłasnością funkcji:

object [[Prototype]]  aFunction.prototype

Niektóre środowiska udostępniają [[Prototyp]] jako __proto__:

anObject.__proto__ === anotherObject

Tworzenia [[prototyp]] link, podczas tworzenia obiektu.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

Te instrukcje są równoważne:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

Tak naprawdę nie możesz zobaczyć link target ( Object.prototype) w nowej instrukcji; zamiast tego cel jest sugerowany przez konstruktor (Object ).

Zapamiętaj:

  • Każdy obiekt ma link, [[Prototyp]] , czasami udostępniany jako __proto__ .
  • Każda funkcja ma prototypewłaściwość, początkowo przechowującą pusty obiekt.
  • Obiekty utworzone za pomocą new są powiązane z prototypewłaściwością ich konstruktora.
  • Jeśli funkcja nigdy nie jest używana jako konstruktor, jej prototypewłaściwość pozostanie nieużywana.
  • Jeśli nie potrzebujesz konstruktora, użyj Object.create zamiast new.

1
Wersja 5 usunęła kilka przydatnych informacji, w tym informacje o Object.create (). Zobacz wersję 4 .
Palec

@Palec co mam dodać?
sam

2
IMO przynajmniej link do Object.create()dokumentów , @sam. Linki do __proto__i Object.prototypebyłyby miłymi ulepszeniami. Podobały mi się twoje przykłady działania prototypów z konstruktorami i Object.create(), ale były to prawdopodobnie długie i mniej istotne elementy, których chciałeś się pozbyć.
Palec

z całej dyskusji, co otrzymuję (pochodzi z klasycznego dziedziczenia), jeśli utworzę funkcję konstruktora i spróbuję utworzyć jej instancję za pomocą nowego operatora, otrzymam tylko metody i właściwości, które zostały dołączone do obiektu proto, dlatego konieczne jest dołączenie całej metody i właściwości do proto obiektu, jeśli chcemy dziedziczyć, prawda?
blackHawk

29

JavaScript nie ma dziedziczenia w zwykłym znaczeniu, ale ma łańcuch prototypów.

łańcuch prototypowy

Jeśli członka obiektu nie można znaleźć w obiekcie, szuka go w łańcuchu prototypów. Łańcuch składa się z innych przedmiotów. Dostęp do prototypu danej instancji można uzyskać za pomocą__proto__ zmiennej. Każdy obiekt ma jeden, ponieważ nie ma różnicy między klasami i instancjami w javascript.

Zaletą dodania funkcji / zmiennej do prototypu jest to, że musi on znajdować się w pamięci tylko raz, a nie dla każdej instancji.

Jest także przydatny do dziedziczenia, ponieważ łańcuch prototypów może składać się z wielu innych obiektów.


1
FF i Chrome obsługują proto , ale nie IE ani Opera.
jakieś

Georg, proszę wyjaśnić dla nooba - „nie ma różnicy między klasami i instancjami w javascript”. - czy mógłbyś opracować? Jak to działa?
Hamish Grubijan

z całej dyskusji, co otrzymuję (pochodzi z klasycznego dziedziczenia), jeśli utworzę funkcję konstruktora i spróbuję utworzyć jej instancję za pomocą nowego operatora, otrzymam tylko metody i właściwości, które zostały dołączone do obiektu proto, dlatego konieczne jest dołączenie całej metody i właściwości do proto obiektu, jeśli chcemy dziedziczyć, prawda?
blackHawk

28

Ten artykuł jest długi. Ale jestem pewien, że wyczyści większość twoich zapytań dotyczących „prototypowej” natury dziedziczenia JavaScript. I nawet więcej. Przeczytaj cały artykuł.

JavaScript zasadniczo ma dwa rodzaje typów danych

  • Bez obiektów
  • Obiekty

Bez obiektów

Poniżej podano typy danych niebędące obiektami

  • strunowy
  • liczba (w tym NaN i Infinity)
  • wartości logiczne (prawda, fałsz)
  • nieokreślony

Te typy danych zwracają następujące, gdy używasz operatora typeof

typeof „string literal” (lub zmienna zawierająca literał ciąg) === „ciąg”

typ 5 (lub dowolny literał liczbowy lub zmienna zawierająca literał liczbowy lub NaN lub Infynity ) === „liczba”

typeof true (lub false lub zmienna zawierająca true lub false ) === „boolean”

typeof undefined (lub niezdefiniowana zmienna lub zmienna zawierająca niezdefiniowany ) === „niezdefiniowany”

W ciąg , liczba i wartość logiczna typy danych mogą być reprezentowane zarówno jako obiekty i dla obiektów .Przy są reprezentowane jako obiekty ich typeof jest zawsze === „przedmiot”. Powrócimy do tego, gdy zrozumiemy typy danych obiektowych.

Obiekty

Typy danych obiektów można dalej podzielić na dwa typy

  1. Obiekty typu funkcji
  2. Obiekty niefunkcjonalne

Obiekty typu Function to te, które zwracają ciąg „function” z operatorem typeof . Do tej kategorii należą wszystkie funkcje zdefiniowane przez użytkownika i wszystkie obiekty wbudowane w JavaScript, które mogą tworzyć nowe obiekty za pomocą nowego operatora. Na przykład

  • Obiekt
  • Strunowy
  • Numer
  • Boolean
  • Szyk
  • Wpisane tablice
  • RegExp
  • Funkcjonować
  • Wszystkie inne wbudowane obiekty, które mogą tworzyć nowe obiekty za pomocą nowego operatora
  • funkcja UserDefinedFunction () {/ * kod zdefiniowany przez użytkownika * /}

Więc typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Function) == = typeof (UserDefinedFunction) === „funkcja”

Wszystkie obiekty typu Function są w rzeczywistości instancjami wbudowanego obiektu JavaScript Function (w tym obiektu Function, tzn. Jest on rekurencyjnie zdefiniowany). To tak, jakby te obiekty zostały zdefiniowane w następujący sposób

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

Jak wspomniano, obiekty typu Funkcja mogą dalej tworzyć nowe obiekty za pomocą nowego operatora . Na przykład obiekt typu Object , String , Number , Boolean , Array , RegExp Or UserDefinedFunction można utworzyć za pomocą

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

W ten sposób tworzone obiekty są wszystkie obiekty typu Funkcja pokoje i powrót ich typeof === „przedmiot” . We wszystkich tych przypadkach obiekt „a” nie może dalej tworzyć obiektów za pomocą operatora new. Więc następujące jest złe

var b=new a() //error. a is not typeof==='function'

Wbudowany obiekt Math to typeof === „obiekt” . Dlatego nowy operator typu Math nie może zostać utworzony przez nowy operator.

var b=new Math() //error. Math is not typeof==='function'

Zauważ również, że funkcje Object , Array i RegExp mogą tworzyć nowy obiekt nawet bez użycia operatora new . Jednak następujące nie.

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

Funkcje zdefiniowane przez użytkownika są szczególnym przypadkiem.

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

Ponieważ obiekty typu Function mogą tworzyć nowe obiekty, nazywane są również Konstruktorami .

Każdy konstruktor / funkcja (wbudowana lub zdefiniowana przez użytkownika), gdy jest zdefiniowana automatycznie, ma właściwość o nazwie „prototyp”, której wartość jest domyślnie ustawiona jako obiekt. Sam obiekt ma właściwość o nazwie „konstruktor”, która domyślnie odwołuje się do konstruktora / funkcji .

Na przykład kiedy definiujemy funkcję

function UserDefinedFunction()
{
}

następuje automatycznie

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

Ta właściwość „prototypowa” jest obecna tylko w obiektach typu Function (i nigdy w obiektach innych niż Function ).

Dzieje się tak, ponieważ gdy tworzony jest nowy obiekt (przy użyciu nowego operatora), dziedziczy on wszystkie właściwości i metody z bieżącego obiektu prototypowego funkcji Constructor, tj. Wewnętrznego odwołania W nowo utworzonym obiekcie tworzone które odwołuje się do obiektu, do którego odwołuje się bieżący obiekt prototypowy funkcji Constructor.

To „wewnętrzne odniesienie”, które jest tworzone w obiekcie do odwoływania się do odziedziczonych właściwości, jest znane jako prototyp obiektu (który odwołuje się do obiektu, do którego odwołuje się właściwość „prototypu” Konstruktora, ale różni się od niego). W przypadku dowolnego obiektu (funkcji lub funkcji) można go pobrać za pomocą metody Object.getPrototypeOf () . Za pomocą tej metody można prześledzić łańcuch prototypowy obiektu.

Ponadto każdy utworzony obiekt ( typ funkcji lub typ niefunkcyjny ) ma właściwość „konstruktora”, która jest dziedziczona z obiektu, do którego odwołuje się właściwość prototypowa funkcji konstruktora. Domyślnie ta właściwość „konstruktora” odwołuje się do funkcji konstruktora, która ją utworzyła (jeśli jest to funkcja konstruktora domyślny „prototyp” nie zostanie zmieniony).

Dla wszystkich obiektów typu Function funkcją konstruktora jest zawsze funkcja Function () {}

W przypadku obiektów niefunkcjonalnych (np. Obiekt JavaScript Built in Math) funkcja konstruktora to funkcja, która go utworzyła. Dla obiektu Math jest to funkcja Object () {} .

Wszystkie powyższe pojęcia mogą być nieco zniechęcające do zrozumienia bez żadnego dodatkowego kodu. Proszę przejść przez następujący kod wiersz po wierszu, aby zrozumieć pojęcie. Spróbuj go wykonać, aby lepiej zrozumieć.

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

Łańcuch prototypów każdego obiektu ostatecznie powraca do Object.prototype (który sam nie ma żadnego obiektu prototypowego). Poniższy kod może służyć do śledzenia łańcucha prototypów obiektu

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

Łańcuch prototypów różnych obiektów działa w następujący sposób.

  • Każdy obiekt Function (w tym wbudowany obiekt Function) -> Function.prototype -> Object.prototype -> null
  • Proste obiekty (utworzone przez nowy obiekt () lub {}, w tym wbudowany obiekt matematyczny) -> Object.prototype -> null
  • Obiekt utworzony za pomocą nowego lub Object.create -> Jeden lub więcej łańcuchów prototypowych -> Object.prototype -> null

Aby utworzyć obiekt bez prototypu, użyj:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

Można by pomyśleć, że ustawienie właściwości prototypowej Konstruktora na wartość null spowoduje utworzenie obiektu z prototypem o wartości NULL. Jednak w takich przypadkach prototyp nowo utworzonego obiektu jest ustawiany na Object.prototype, a jego konstruktor jest ustawiony na funkcję Object. Pokazuje to poniższy kod

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

Poniżej w podsumowaniu tego artykułu

  • Istnieją dwa typy obiektów Typy funkcji i Typy niefunkcjonalne
  • Tylko obiekty typu Funkcja mogą utworzyć nowy obiekt za pomocą operatora new . Obiekty utworzone w ten sposób są obiektami typu Non Function . Obiekty typu Non Function nie mogą dalej tworzyć obiektu za pomocą operatora new .

  • Wszystkie obiekty typu Funkcja domyślnie mają właściwość „prototypu” . Ta właściwość „prototypowa” odwołuje się do obiektu, który ma właściwość „konstruktora”, która domyślnie odwołuje się do samego obiektu typu Funkcja .

  • Wszystkie obiekty ( typ funkcji i typ inny niż funkcja ) mają właściwość „konstruktor”, która domyślnie odwołuje się do obiektu typu obiekt / konstruktor, który go utworzył.

  • Każdy obiekt, który zostanie utworzony wewnętrznie, odwołuje się do obiektu, do którego odnosi się właściwość „prototypu” konstruktora, który go utworzył. Ten obiekt jest znany jako prototyp utworzonego obiektu (który różni się od właściwości „prototypowej” obiektów typu Function, do której się odwołuje). W ten sposób utworzony obiekt może uzyskać bezpośredni dostęp do metod i właściwości zdefiniowanych w obiekcie, do którego odwołuje się właściwość „prototypu” Konstruktora (w momencie tworzenia obiektu).

  • Wykonania prototypu obiektu (i stąd jego nazwy właściwości dziedziczne) mogą być pobierane za pomocą Object.getPrototypeOf () metody. W rzeczywistości ta metoda może być używana do poruszania się po całym łańcuchu prototypów obiektu.

  • Łańcuch prototypów każdego obiektu ostatecznie powraca do Object.prototype (chyba że obiekt jest tworzony za pomocą Object.create (null), w którym to przypadku obiekt nie ma prototypu).

  • typeof (new Array ()) === „obiekt” wynika z projektu języka, a nie z pomyłki wskazanej przez Douglasa Crockforda

  • Ustawienie właściwości prototypu Konstruktora na null (lub niezdefiniowany, liczba, prawda, fałsz, łańcuch) nie spowoduje utworzenia obiektu z prototypem o wartości NULL. W takich przypadkach prototyp nowo utworzonego obiektu jest ustawiany na Object.prototype, a jego konstruktor jest ustawiony na funkcję Object.

Mam nadzieję że to pomoże.


24

Koncepcja prototypaldziedziczenia jest jedną z najbardziej skomplikowanych dla wielu programistów. Spróbujmy zrozumieć źródło problemu, aby prototypal inheritancelepiej zrozumieć . Zacznijmy od plainfunkcji.

wprowadź opis zdjęcia tutaj

Jeśli używamy newoperatora na Tree function, nazywamy go jako constructorfunkcję.

wprowadź opis zdjęcia tutaj

Każda JavaScriptfunkcja ma prototype. Po zalogowaniu Tree.prototypeotrzymujesz ...

wprowadź opis zdjęcia tutaj

Jeśli spojrzysz na powyższe console.log()dane wyjściowe, możesz zobaczyć właściwość konstruktora Tree.prototypei __proto__właściwość również. __proto__Reprezentuje prototypeże ta functionoparta jest wyłączony, a ponieważ jest to po prostu zwykły JavaScript functionbez inheritanceskonfigurować jeszcze, że odnosi się do Object prototypeco jest czymś po prostu wbudowane w JavaScript ...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

Ma takie rzeczy jak .toString, .toValue, .hasOwnPropertyitp ...

__proto__który został przyniesiony moja mozilla jest przestarzała i jest zastąpiona przez Object.getPrototypeOfmetodę uzyskania object's prototype.

wprowadź opis zdjęcia tutaj

Object.getPrototypeOf(Tree.prototype); // Object {} 

Dodajmy metodę do naszego Tree prototype.

wprowadź opis zdjęcia tutaj

Zmodyfikowaliśmy Rooti dodaliśmy functiondo niego gałąź.

wprowadź opis zdjęcia tutaj

To znaczy, kiedy utworzyć instancez Tree, można nazwać to za branchmetody.

wprowadź opis zdjęcia tutaj

Możemy również dodać primitiveslub objectsdo naszego Prototype.

wprowadź opis zdjęcia tutaj

Dodajmy child-treedo naszego Tree.

wprowadź opis zdjęcia tutaj

Tutaj Childdziedziczy prototypepo drzewie, my tutaj robimy Object.create()metodą tworzenia nowego obiektu na podstawie tego, co przekazujesz, oto jest Tree.prototype. W tym przypadku robimy ustawienie prototypu Childa na nowy obiekt, który wygląda identycznie jak Treeprototyp. Następnie ustawiamy Child's constructor to Child, jeśli nie, wskazuje Tree().

wprowadź opis zdjęcia tutaj

Childteraz ma swoją własną prototype, __proto__punkty do Treei Tree's prototypepunkty do bazy Object.

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

Teraz można utworzyć instancez Childzawołanie branch, który jest dostępny w oryginalnie Tree. Tak naprawdę nie zdefiniowaliśmy naszego branchw Child prototype. ALE, w Root prototypektórym dziedziczy Dziecko.

wprowadź opis zdjęcia tutaj

W JS wszystko nie jest przedmiotem, wszystko może działać jak obiekt.

Javascriptma takie prymitywy jak strings, number, booleans, undefined, null.nie są object(i.e reference types), ale z pewnością mogą zachowywać się jak object. Spójrzmy na przykład tutaj.

wprowadź opis zdjęcia tutaj

W pierwszym wierszu tego wpisu primitivedo nazwy przypisana jest wartość ciągu. Druga linia traktuje nazwę jak objecti wywołuje charAt(0)za pomocą notacji kropkowej.

Oto, co dzieje się za kulisami: // co JavaScriptrobi silnik

wprowadź opis zdjęcia tutaj

String objectIstnieje tylko jedno oświadczenie zanim zostanie zniszczony (proces zwany autoboxing). Wróćmy do naszego prototypal inheritance.

  • Javascriptobsługuje dziedziczenie poprzez delegationna podstawie prototypes.
  • Każda Functionma prototypewłaściwość, która odnosi się do innego obiektu.
  • properties/functionssą oglądane z objectsamego siebie lub za pośrednictwem prototypełańcucha, jeśli nie istnieje

A prototypew JS jest obiektem, który yieldsty do rodzica innego object. [tzn. delegacja] Delegation oznacza, że ​​jeśli nie jesteś w stanie nic zrobić, powiesz komuś innemu, aby zrobił to za Ciebie.

wprowadź opis zdjęcia tutaj

https://jsfiddle.net/say0tzpL/1/

Jeśli spojrzysz na powyższe skrzypce, pies ma dostęp do toStringmetody, ale nie jest w nim dostępna, ale jest dostępna za pośrednictwem łańcucha prototypów, do którego deleguje sięObject.prototype

wprowadź opis zdjęcia tutaj

Jeśli spojrzysz na poniższy, staramy się uzyskać dostęp do callmetody, która jest dostępna w każdym function.

wprowadź opis zdjęcia tutaj

https://jsfiddle.net/rknffckc/

Jeśli spojrzysz na powyższe skrzypce, Profilefunkcja ma dostęp do callmetody, ale nie jest w niej dostępna, ale dostępna za pośrednictwem łańcucha prototypów, do którego deleguje sięFunction.prototype

wprowadź opis zdjęcia tutaj

Uwaga: prototype jest właściwością konstruktora funkcji, natomiast __proto__jest własnością obiektów zbudowanych z konstruktora funkcji. Każda funkcja ma prototypewłaściwość, której wartość jest pusta object. Kiedy tworzymy wystąpienie funkcji, otrzymujemy właściwość wewnętrzną [[Prototype]]lub do __proto__której odwołania jest prototyp Funkcji constructor.

wprowadź opis zdjęcia tutaj

Powyższy schemat wygląda na nieco skomplikowany, ale pokazuje cały obraz tego, jak to zrobić prototype chaining działa. Przejdźmy przez to powoli:

Istnieją dwa przykład b1a b2, którego konstruktor jest Bari nadrzędne foo i ma dwie metody z łańcucha prototypów identifyi speakpoprzez BariFoo

wprowadź opis zdjęcia tutaj

https://jsfiddle.net/kbp7jr7n/

Jeśli spojrzysz na powyższy kod, mamy Fookonstruktora, który ma metodę identify()i Barkonstruktora, który ma speakmetodę. Tworzymy dwa Barinstancji b1i b2którego typ jest dominująca Foo. Teraz, podczas wywoływania speakmetody Bar, jesteśmy w stanie zidentyfikować, kto dzwoni do mówienia za pośrednictwem prototypełańcucha.

wprowadź opis zdjęcia tutaj

Barteraz ma wszystkie metody, Fooktóre są zdefiniowane w jego prototype. Chodźmy kopać dalej w zrozumieniu Object.prototypei Function.prototypei jak są one powiązane. Jeśli przyjrzeć się konstruktora Foo, Bari ObjectFunction constructor.

wprowadź opis zdjęcia tutaj

prototypeO Barto Foo, prototypeze Foojest w stanie Objecti jeśli spojrzeć bacznie prototypez Foojest związane Object.prototype.

wprowadź opis zdjęcia tutaj

Zanim zamkniemy to, po prostu napiszmy tutaj mały fragment kodu, aby podsumować wszystko powyżej . Używamy instanceoftutaj operatora, aby sprawdzić, czy objectma w swoim prototypełańcuchu prototypewłaściwość tego, constructorktóry poniżej podsumowuje cały duży diagram.

wprowadź opis zdjęcia tutaj

Mam nadzieję, że ten dodatek zawiera pewne informacje, wiem, że ten rodzaj może być duży do uchwycenia ... w prostych słowach to tylko obiekty powiązane z obiektami !!!!


22

jaki jest dokładnie cel tej właściwości „.prototype”?

Interfejs do klas standardowych staje się rozszerzalny. Na przykład używaszArray klasy i musisz również dodać niestandardowy serializator dla wszystkich obiektów tablicy. Czy spędzasz czas na kodowaniu podklasy, czy używasz kompozycji lub ... Właściwość prototyp rozwiązuje to, pozwalając użytkownikom kontrolować dokładny zestaw elementów / metod dostępnych dla klasy.

Pomyśl o prototypach jako o dodatkowym wskaźniku vtable. Gdy brakuje niektórych członków oryginalnej klasy, prototyp jest sprawdzany w czasie wykonywania.


21

Może to pomóc w podzieleniu łańcuchów prototypów na dwie kategorie.

Zastanów się nad konstruktorem:

 function Person() {}

Wartość Object.getPrototypeOf(Person)jest funkcją. W rzeczywistości tak jest Function.prototype. Ponieważ Personzostał utworzony jako funkcja, ma ten sam prototypowy obiekt funkcji, co wszystkie funkcje. Jest taki sam jak Person.__proto__, ale tej właściwości nie należy używać. W każdym razie wraz z Object.getPrototypeOf(Person)tobą skutecznie wejdź po drabinie łańcucha prototypów.

Łańcuch w górę wygląda następująco:

    PersonFunction.prototypeObject.prototype(punkt końcowy)

Ważne jest to, że ten łańcuch prototypów ma niewiele wspólnego z obiektami, które Personmożna konstruować . Te skonstruowane obiekty mają swój własny łańcuch prototypowy, a ten łańcuch może potencjalnie nie mieć wspólnego bliskiego przodka z tym wspomnianym powyżej.

Weźmy na przykład ten obiekt:

var p = new Person();

p nie ma bezpośredniego związku łańcucha prototypowego z Osobą . Ich związek jest inny. Obiekt p ma własny łańcuch prototypów. Za pomocą Object.getPrototypeOfzobaczysz, że łańcuch jest następujący:

    pPerson.prototypeObject.prototype(punkt końcowy)

W tym łańcuchu nie ma obiektu funkcyjnego (chociaż może to być).

Więc Personwydaje się związane z dwóch rodzajów łańcuchów, które żyją własnym życiem. Aby „przeskoczyć” z jednego łańcucha na drugi, użyj:

  1. .prototype: przeskocz z łańcucha konstruktora do łańcucha utworzonego obiektu. Ta właściwość jest więc zdefiniowana tylko dla obiektów funkcji (jak newmożna jej używać tylko w przypadku funkcji).

  2. .constructor: przeskocz z łańcucha utworzonego obiektu do łańcucha konstruktora.

Oto wizualna prezentacja dwóch zaangażowanych prototypowych łańcuchów, przedstawionych jako kolumny:

wprowadź opis zdjęcia tutaj

Podsumować:

prototypeNieruchomość daje żadnych informacji o pacjenta łańcucha prototypów, ale obiektów stworzonych przez pacjenta.

Nic dziwnego, że nazwa nieruchomości prototypemoże prowadzić do zamieszania. Być może byłoby lepiej, gdyby ta właściwość została nazwana prototypeOfConstructedInstanceslub coś w tym stylu.

Możesz przeskakiwać między dwoma łańcuchami prototypowymi:

Person.prototype.constructor === Person

Tę symetrię można przełamać, jednoznacznie przypisując do obiektu inny obiekt prototype właściwości (więcej na ten temat później).

Utwórz jedną funkcję, zdobądź dwa obiekty

Person.prototypeto obiekt, który został utworzony w tym samym czasie, w którym funkcja Personzostała utworzona. Ma Personjako konstruktor, chociaż ten konstruktor jeszcze się nie uruchomił. Dwa obiekty są tworzone jednocześnie:

  1. Funkcja Personsama
  2. Obiekt, który będzie działał jako prototyp, gdy funkcja zostanie wywołana jako konstruktor

Oba są obiektami, ale mają różne role: konstruuje obiekt funkcji , podczas gdy drugi obiekt reprezentuje prototyp dowolnego obiektu, który zbuduje funkcja. Prototypowy obiekt stanie się rodzicem konstruowanego obiektu w łańcuchu prototypów.

Ponieważ funkcja jest również przedmiotem, ma również swojego rodzica we własnym łańcuchu prototypów, ale pamiętaj, że te dwa łańcuchy dotyczą różnych rzeczy.

Oto niektóre równania, które mogą pomóc w zrozumieniu problemu - wszystkie z nich drukują true:

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);

Dodawanie poziomów do łańcucha prototypów

Chociaż obiekt prototypowy jest tworzony podczas tworzenia funkcji konstruktora, można go zignorować i przypisać inny obiekt, który powinien być użyty jako prototyp dla kolejnych instancji utworzonych przez ten konstruktor.

Na przykład:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

Teraz łańcuch prototypowy t jest o jeden krok dłuższy niż łańcuch p :

    tpPerson.prototypeObject.prototype(punkt końcowy)

Drugi łańcuch prototypów już nie jest: Thiefi Personczy rodzeństwo ma tego samego rodzica w swoim łańcuchu prototypów:

    Person}
    Thief  } → Function.prototypeObject.prototype(punkt końcowy)

Wcześniej prezentowaną grafikę można następnie rozszerzyć na to (oryginał Thief.prototypejest pominięty):

wprowadź opis zdjęcia tutaj

Niebieskie linie reprezentują prototypowe łańcuchy, inne kolorowe linie reprezentują inne relacje:

  • między obiektem a jego konstruktorem
  • między konstruktorem a prototypowym obiektem, który będzie używany do konstruowania obiektów


16

Przydało mi się wyjaśnienie „łańcucha prototypów” jako konwencji rekurencyjnej, gdy obj_n.prop_Xsię do niego odwołuje:

jeśli obj_n.prop_Xnie istnieje, sprawdź obj_n+1.prop_Xgdzieobj_n+1 = obj_n.[[prototype]]

Jeśli w prop_Xkońcu zostanie znaleziony w k-tym prototypowym obiekcie, to

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

Wykres zależności obiektów JavaScript według ich właściwości można znaleźć tutaj:

Wykres obiektów js

http://jsobjects.org


14

Kiedy konstruktor tworzy obiekt, obiekt ten domyślnie odwołuje się do właściwości „prototypu” konstruktora w celu rozstrzygania odwołań do właściwości. Do właściwości „prototypu” konstruktora może odwoływać się wyrażenie programowe konstruktor.prototyp, a właściwości dodane do prototypu obiektu są współdzielone, poprzez dziedziczenie, przez wszystkie obiekty współdzielące prototyp.


11

Istnieją tutaj dwa odrębne, ale powiązane podmioty, które wymagają wyjaśnienia:

  • .prototypeWłasność funkcji.
  • Właściwość [[Prototype]][1] wszystkich obiektów [2] .

To są dwie różne rzeczy.

[[Prototype]]Nieruchomości:

Jest to właściwość istniejąca na wszystkich [2] obiektach.

To, co jest tutaj przechowywane, to inny obiekt, który jako sam obiekt ma [[Prototype]]swój własny, który wskazuje na inny obiekt. Ten drugi obiekt ma [[Prototype]]swój własny. Ta historia trwa, dopóki nie dotrzesz do prototypowego obiektu, który zapewnia metody dostępne dla wszystkich obiektów (np .toString.).

[[Prototype]]Nieruchomość jest częścią tego, co tworzy [[Prototype]]łańcuch. Ten łańcuch [[Prototype]]obiektów jest to, co jest badane, gdy, na przykład, [[Get]]czy [[Set]]operacje są wykonywane na obiekcie:

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain

.prototypeNieruchomości:

Jest to właściwość, która występuje tylko w funkcjach. Korzystanie z bardzo prostej funkcji:

function Bar(){};

.prototypeNieruchomość posiada obiekt , który zostanie przypisany do b.[[Prototype]]kiedy to zrobić var b = new Bar. Możesz to łatwo sprawdzić:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true

Jednym z najważniejszych .prototypes jest to, że z Objectfunkcji . Ten prototyp zawiera obiekt prototypowy, który [[Prototype]]zawierają wszystkie łańcuchy. Na nim zdefiniowane są wszystkie dostępne metody dla nowych obiektów:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))

Ponieważ .prototypejest to obiekt, ma [[Prototype]]właściwość. Jeśli nie dokonywać żadnych przypisanych do Function.prototypetego, .prototype„s [[Prototype]]punktów do prototypowego obiektu (Object.prototype ). Jest to wykonywane automatycznie za każdym razem, gdy tworzysz nową funkcję.

W ten sposób za każdym razem, gdy wykonujesz new Bar;łańcuch prototypów, masz wszystko zdefiniowane Bar.prototypei wszystko zdefiniowane na Object.prototype:

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)

Kiedy robisz przypisania doFunction.prototype wszystkich robisz jest wydłużenie łańcucha prototypów zawierać inny obiekt. To jest jak wstawienie na pojedynczo połączonej liście.

To w zasadzie zmienia [[Prototype]]łańcuch, pozwalając właściwościom zdefiniowanym na obiekcie przypisanym, Function.prototypeaby były widoczne dla dowolnego obiektu utworzonego przez funkcję.


[1: To nikogo nie dezorientuje; udostępnione za pośrednictwem tej __proto__własności w wielu implementacjach.
[2]: Wszystkie oprócz null.


10

Pozwól, że powiem ci, jak rozumiem prototypy. Nie zamierzam porównywać spadku tutaj z innymi językami. Chciałbym, żeby ludzie przestali porównywać języki i po prostu zrozumieli język jako taki. Zrozumienie prototypów i dziedziczenia prototypów jest tak proste, jak pokażę poniżej.

Prototyp jest jak model, na podstawie którego tworzysz produkt. Kluczową kwestią do zrozumienia jest to, że kiedy tworzysz obiekt przy użyciu innego obiektu jako jego prototypu, połączenie między prototypem a produktem jest trwałe. Na przykład:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Każdy obiekt zawiera wewnętrzną właściwość o nazwie [[prototyp]], do której Object.getPrototypeOf()funkcja może uzyskać dostęp . Object.create(model)tworzy nowy obiekt i ustawia jego właściwość [[prototyp]] na model obiektu . Kiedy to zrobisz Object.getPrototypeOf(product), otrzymasz model obiektowy .

Właściwości produktu są obsługiwane w następujący sposób:

  • Gdy właściwość jest otwierana tylko w celu odczytania jej wartości, jest ona sprawdzana w łańcuchu zasięgu. Poszukiwanie zmiennej rozpoczyna się od produktu w górę do jego prototypu. Jeśli taka zmienna zostanie znaleziona podczas wyszukiwania, wyszukiwanie zostaje zatrzymane i zwracana jest wartość. Jeśli takiej zmiennej nie można znaleźć w łańcuchu zasięgu, zwracana jest wartość niezdefiniowana.
  • Kiedy właściwość jest zapisywana (zmieniana), wówczas właściwość jest zawsze zapisywana na obiekcie produktu . Jeśli produkt nie ma już takiej właściwości, jest domyślnie tworzony i zapisywany.

Takie łączenie obiektów za pomocą właściwości prototypu nazywa się dziedziczeniem prototypowym. Tam jest to takie proste, zgadzasz się?


Nie zawsze napisane na produkcie przy zleceniu. Nie wyjaśniasz jasno, że należy zainicjować określonych członków instancji, a członkowie udostępnieni mogą przejść do prototypu. Zwłaszcza gdy masz specyficzne dla instancji zmienne elementy: stackoverflow.com/questions/16063394/...
HMR

HMR: W twoim przykładzie w twojej odpowiedzi ben.food.push („Hamburger”); linia zmienia właściwość obiektu prototypowego z następujących powodów: 1.) Najpierw przeglądany jest plik ben.food, a każda akcja wyszukiwania po prostu przeszukuje łańcuch zasięgu. 2.) Wykonywana jest funkcja push tego obiektu ben.food. Pisząc tryb w mojej odpowiedzi, mam na myśli, gdy wyraźnie ustawiasz dla niego wartość, jak w: ben.food = ['Idly']; Spowoduje to zawsze utworzenie nowej właściwości (jeśli jeszcze jej nie ma) na obiekcie produktu, a następnie przypisanie jej wartości.
Aravind,

HMR: Dziękuję za komentarz, zmusił mnie do przemyślenia i przetestowania mojego zrozumienia.
Aravind,

Podczas ponownego przypisywania ben.food spowoduje to cień elementu żywności, chyba że jedzenie jest tworzone za pomocą Object.defineProperty, Object.defineProperties lub Object.create z drugim argumentem (więc nie zawsze). Możesz nawet zmienić prototyp za pomocą (jak to wygląda) ponownego przypisania, gdy tworzysz program ustawiający getter. Jeśli chodzi o wzorce dziedziczenia, rozumiem, że funkcja konstruktora jest trudna do zrozumienia i ma pewne poważne problemy, ale dobrze, jeśli ją rozumiesz. Dziedziczenie w JavaScript nie zaczyna się i nie kończy od ustawienia prototypu, należy również (ponownie) inicjalizować (konstruktory).
HMR

Twoja odpowiedź jest dobra w wyjaśnianiu prototypu, ale może zostać źle zinterpretowana poprzez uproszczenie dziedziczenia w JavaScript i określonych elementach instancji. Zadano wiele pytań, dlaczego mutowanie prototypowego elementu na instancji wpływa na inne instancje.
HMR


10

Rozważ następujący keyValueStoreobiekt:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Mogę utworzyć nową instancję tego obiektu, wykonując następujące czynności:

kvs = keyValueStore.create();

Każde wystąpienie tego obiektu miałoby następujące właściwości publiczne:

  • data
  • get
  • set
  • delete
  • getLength

Załóżmy teraz, że utworzymy 100 wystąpień tego keyValueStoreobiektu. Nawet jeśli get, set, delete, getLengthzrobi dokładnie to samo dla każdego z tych 100 przypadków, każdy przypadek ma swoją własną kopię tej funkcji.

Teraz wyobraźcie sobie, że można mieć tylko jeden get, set, deletea getLengthkopię, a każdy przypadek będzie odwołać tę samą funkcję. Byłoby to lepsze dla wydajności i wymaga mniej pamięci.

Tam właśnie pojawiają się prototypy. Prototyp to „schemat” właściwości, który jest dziedziczony, ale nie jest kopiowany przez instancje. Oznacza to, że istnieje tylko raz w pamięci dla wszystkich instancji obiektu i jest współużytkowany przez wszystkie te instancje.

Teraz keyValueStoreponownie rozważ przedmiot. Mógłbym przepisać to w ten sposób:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Robi to DOKŁADNIE tak samo jak poprzednia wersja keyValueStoreobiektu, z tym wyjątkiem, że wszystkie jego metody są teraz umieszczone w prototypie. Oznacza to, że wszystkie ze 100 instancji korzystają teraz z tych czterech metod, zamiast mieć własną kopię.


9

Podsumowanie:

  • Funkcje są obiektami w javascript, a zatem mogą mieć właściwości
  • Funkcje (Konstruktor) zawsze mają właściwość prototypową
  • Gdy funkcja jest używana jako konstruktor ze newsłowem kluczowym, obiekt otrzymuje prototyp. Odniesienie do tego prototypu można znaleźć we __proto__właściwości nowo utworzonego obiektu.
  • Ta __proto__właściwość odnosi się do prototypewłaściwości funkcji konstruktora.

Przykład:

function Person (name) {
  this.name = name;
}

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.

Dlaczego jest to przydatne:

JavaScript ma mechanizm wyszukiwania obiektów nazywa się „dziedziczeniem prototypowym”. Oto, co właściwie robi:

  • Najpierw sprawdzane jest, czy właściwość znajduje się na samym obiekcie. Jeśli tak, ta właściwość jest zwracana.
  • Jeśli właściwość nie znajduje się na samym obiekcie, „wspina się po protochainie”. Zasadniczo patrzy na obiekt, do którego odnosi się właściwość proto . Tam sprawdza, czy właściwość jest dostępna na obiekcie, do którego odnosi się proto
  • Jeśli właściwość nie znajduje się na obiekcie proto , będzie wspinać się po łańcuchu proto aż do obiektu Object.
  • Jeśli nie może znaleźć właściwości nigdzie w obiekcie, a jej łańcuch prototypów zwróci niezdefiniowany.

Na przykład:

function Person(name) {
  this.name = name;
}

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);

Aktualizacja:

__proto__Nieruchomość została zaniechana, chociaż jest realizowane w większości nowoczesnych przeglądarek lepszy sposób, aby uzyskać odniesienie prototyp obiektu będzie:

Object.getPrototypeOf()


7

Zawsze lubię analogie, jeśli chodzi o rozumienie tego rodzaju rzeczy. Moim zdaniem „dziedziczenie prototypów” jest dość mylące w porównaniu z dziedziczeniem basów klasowych, mimo że prototypy są znacznie prostszym paradygmatem. W rzeczywistości w przypadku prototypów tak naprawdę nie ma dziedziczenia, więc nazwa sama w sobie wprowadza w błąd, jest raczej rodzajem „delegacji”.

Wyobraź to sobie ....

Jesteś w liceum, jesteś w klasie i masz dzisiaj quiz, ale nie masz pióra do wypełnienia swoich odpowiedzi. Doh!

Siedzisz obok swojego przyjaciela Finniusa, który może mieć długopis. Pytasz, a on bezskutecznie rozgląda się po biurku, ale zamiast powiedzieć „nie mam długopisu”, jest miłym przyjacielem i sprawdza, czy ma długopis z drugim przyjacielem Derpem. Derp rzeczywiście ma zapasowy długopis i przekazuje go z powrotem do Finnius, który przekazuje go tobie, aby ukończyć quiz. Derp powierzył pióro długopisowi Finnius, który przekazał ci pióro do użycia.

Ważne jest tutaj to, że Derp nie daje ci długopisu, ponieważ nie masz z nim bezpośredniej relacji .

Jest to uproszczony przykład działania prototypów, w których drzewo danych jest przeszukiwane w celu znalezienia szukanej rzeczy.


3

inny schemat pokazujący __proto__ , relacje prototypu i konstruktora : wprowadź opis zdjęcia tutaj


1

Po prostu masz już obiekt, Object.newale nadal nie masz obiektu, gdy używasz składni konstruktora.


1

Ważne jest, aby zrozumieć, że istnieje rozróżnienie między prototypem obiektu (który jest dostępny za pośrednictwem Object.getPrototypeOf(obj)lub za pośrednictwem przestarzałej __proto__właściwości) a prototypewłaściwością funkcji konstruktora. Pierwsza z nich jest własnością każdej instancji, a druga jest własnością konstruktora. Oznacza to, że Object.getPrototypeOf(new Foobar())odnosi się do tego samego obiektu co Foobar.prototype.

Odniesienie: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes


0

Prototype tworzy nowy obiekt przez klonowanie istniejącego obiektu . Tak naprawdę, kiedy myślimy o prototypie, możemy naprawdę pomyśleć o klonowaniu lub zrobieniu kopii czegoś zamiast go wymyślić.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.