it-swarm.com.de

Wie funktioniert das JavaScript-Protokoll?

Ich beschäftige mich nicht mit dynamischen Programmiersprachen, aber ich habe meinen Anteil an JavaScript-Code geschrieben. Ich habe mich nie wirklich mit dieser prototypenbasierten Programmierung befasst. Weiß jemand, wie das funktioniert? 

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

Ich erinnere mich an eine Menge Diskussionen, die ich vor einiger Zeit mit Leuten hatte (ich bin mir nicht ganz sicher, was ich mache), aber so wie ich es verstehe, gibt es keine Vorstellung von einer Klasse. Es ist nur ein Objekt, und Instanzen dieser Objekte sind Klone des Originals, richtig?

Was ist der genaue Zweck dieser Eigenschaft ".prototype" in JavaScript? Wie hängt es mit der Instanziierung von Objekten zusammen?

Update: richtiger Weg

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

Auch diese Folien haben wirklich sehr geholfen.

1918
John Leidegren

Jedes JavaScript-Objekt hat eine interne Eigenschaft namens [[Prototyp]] . Wenn Sie eine Eigenschaft über obj.propName oder obj['propName'] suchen und das Objekt über keine solche Eigenschaft verfügt, die über obj.hasOwnProperty('propName') geprüft werden kann, sucht die Laufzeitumgebung die Eigenschaft in dem Objekt, das stattdessen von [[Prototyp]] referenziert wird. Wenn das Prototyp-Objekt auch keine solche Eigenschaft besitzt, wird sein Prototyp überprüft und die Prototyp-Kette des ursprünglichen Objekts durchlaufen, bis eine Übereinstimmung gefunden oder das Ende erreicht wird.

Einige JavaScript-Implementierungen ermöglichen den direkten Zugriff auf die Eigenschaft [[Prototyp]], z. B. über eine nicht standardmäßige Eigenschaft mit dem Namen __proto__. Im Allgemeinen ist es nur möglich, den Prototyp eines Objekts während der Objekterstellung festzulegen: Wenn Sie ein neues Objekt über new Func() erstellen, wird die Eigenschaft [[Prototyp]] des Objekts auf das mit Func.prototype referenzierte Objekt festgelegt.

Dies ermöglicht das Simulieren von Klassen in JavaScript, obwohl das Vererbungssystem von JavaScript - wie wir gesehen haben - prototypisch und nicht klassenbasiert ist:

Stellen Sie sich Konstruktorfunktionen als Klassen und die Eigenschaften des Prototyps (dh des Objekts, auf das die prototype-Eigenschaft der Konstruktorfunktion verweist) als gemeinsam genutzte Member vor, dh Member, die für jede Instanz gleich sind. In klassenbasierten Systemen werden Methoden für jede Instanz auf dieselbe Weise implementiert. Daher werden normalerweise Methoden zum Prototyp hinzugefügt, während die Felder eines Objekts instanzspezifisch sind und daher während der Konstruktion zum Objekt selbst hinzugefügt werden.

958
Christoph

In einer Sprache, die klassische Vererbung wie Java, C # oder C++ implementiert, erstellen Sie zunächst eine Klasse - einen Bauplan für Ihre Objekte - und dann können Sie neue Objekte aus dieser Klasse erstellen oder Sie können die Klasse erweitern, indem Sie eine neue Klasse definieren, die sich erweitert die ursprüngliche Klasse.

In JavaScript erstellen Sie zuerst ein Objekt (es gibt kein Klassenkonzept), dann können Sie Ihr eigenes Objekt erweitern oder daraus neue Objekte erstellen. Es ist nicht schwierig, aber ein wenig fremd und schwer zu verstoffwechseln für jemanden, der sich auf klassische Weise gewöhnt hat.

Beispiel:

//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();

Bis jetzt habe ich das Basisobjekt erweitert, jetzt erstelle ich ein anderes Objekt und erbt dann von 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());

var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Wie gesagt, kann ich setAmountDue () und getAmountDue () für eine Person nicht aufrufen.

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

Dies ist ein sehr einfaches, prototypbasiertes Objektmodell, das bei der Erläuterung als Beispiel betrachtet wird. Es gibt noch keinen Kommentar:

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

Es gibt einige wichtige Punkte, die wir berücksichtigen müssen, bevor wir das Prototypenkonzept durchgehen.

1- So funktionieren die JavaScript-Funktionen tatsächlich:

Um den ersten Schritt zu machen, müssen wir herausfinden, wie JavaScript-Funktionen tatsächlich funktionieren, als klassenähnliche Funktion mit dem Schlüsselwort this oder einfach als reguläre Funktion mit ihren Argumenten, was es tut und was es zurückgibt.

Angenommen, wir möchten ein Person-Objektmodell erstellen. In diesem Schritt versuche ich jedoch, genau dasselbe zu tun, ohne die Schlüsselwörter prototype und new zu verwenden.

In diesem Schritt functions, objects und this sind alle wir haben.

Die erste Frage wäre , wie das Schlüsselwort this nützlich sein könnte, ohne das Schlüsselwort new zu verwenden.

Um das zu beantworten, nehmen wir an, wir haben ein leeres Objekt und zwei Funktionen wie:

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

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

und jetzt ohne das Schlüsselwort new, wie wir diese Funktionen verwenden könnten. So hat JavaScript 3 verschiedene Möglichkeiten, dies zu tun:

ein. Der erste Weg ist, die Funktion als reguläre Funktion aufzurufen:

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

in diesem Fall ist dies das aktuelle Kontextobjekt. Dies ist normalerweise das globale Objekt window im Browser oder GLOBAL in Node.js. Das bedeutet, wir hätten window.name im Browser oder GLOBAL.name in Node.js mit "George" als Wert.

b. Wir können sie als ihre Eigenschaften an ein Objekt anhängen

- Der einfachste Weg , dies zu tun, besteht darin, das leere person-Objekt zu ändern, wie zum Beispiel:

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

so können wir sie nennen wie:

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

und jetzt ist das person-Objekt wie folgt:

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

- Die andere Möglichkeit, eine Eigenschaft an ein Objekt anzuhängen, ist die Verwendung von prototype dieses Objekts, das in jedem JavaScript-Objekt mit dem Namen __proto__ zu finden ist, und ich habe versucht, dies zu erklären es ein bisschen auf den zusammenfassenden Teil. So könnten wir das ähnliche Ergebnis erhalten, indem wir Folgendes tun:

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

Aber auf diese Weise modifizieren wir tatsächlich den Object.prototype, da jedes Mal, wenn wir ein JavaScript-Objekt mit Literalen ({ ... }) erstellen, es auf Basis von Object.prototype erstellt wird, was bedeutet, dass es angehängt wird auf das neu erstellte Objekt als Attribut mit dem Namen __proto__. Wenn wir es also ändern, wie wir es in unserem vorherigen Code-Snippet getan haben, werden alle JavaScript-Objekte geändert, kein gutes trainieren. Was könnte nun die bessere Praxis sein:

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

und jetzt sind andere Objekte in Frieden, aber es scheint immer noch keine gute Praxis zu sein. Wir haben also noch eine weitere Lösung, aber um diese Lösung zu verwenden, sollten wir zu der Codezeile zurückkehren, in der das Objekt person erstellt wurde (var person = {};). Ändern Sie es dann wie folgt:

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

dazu wird ein neues JavaScript Object erstellt und das Attribut propertiesObject an das Attribut __proto__ angehängt. So stellen Sie sicher, dass Sie Folgendes tun können:

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

Der knifflige Punkt hierbei ist jedoch, dass Sie Zugriff auf alle Eigenschaften haben, die in __proto__ auf der ersten Ebene des person-Objekts definiert sind (lesen Sie den Zusammenfassungsteil für weitere Details).


wie Sie sehen, verweist this auf das Objekt person.

c. JavaScript bietet eine andere Möglichkeit, die Funktion mit this bereitzustellen. Dabei wird Aufruf oder Anwenden zum Aufrufen der Funktion verwendet.

Die Methode apply () ruft eine Funktion mit diesem Wert und Argumenten auf, die als Array (oder Array-ähnliches Objekt) bereitgestellt werden.

und

Die call () -Methode ruft eine Funktion mit einem bestimmten Wert und einzeln angegebenen Argumenten auf.

auf diese weise, die mir am besten gefällt, können wir unsere funktionen leicht wie folgt aufrufen:

Person.call(person, "George");

oder

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

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

diese drei Methoden sind die wichtigsten ersten Schritte, um die .prototype-Funktionalität zu ermitteln.


2- Wie funktioniert das Schlüsselwort new?

dies ist der zweite Schritt, um die .prototype -Funktionalität zu verstehen. Dies ist, was ich verwende, um den Prozess zu simulieren:

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

in diesem Teil versuche ich, alle Schritte auszuführen, die JavaScript ausführt, ohne das Schlüsselwort new und prototype zu verwenden, wenn Sie das Schlüsselwort new verwenden. Wenn wir also new Person("George") ausführen, fungiert die Funktion Person als Konstruktor.

ein. zuallererst macht es ein leeres Objekt, im Grunde genommen einen leeren Hash wie:

var newObject = {};

b. Der nächste Schritt, den JavaScript ausführt, ist das Anhängen aller Prototypobjekte an das neu erstellte Objekt

wir haben my_person_prototype hier ähnlich dem Prototyp-Objekt.

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

Es ist nicht die Art und Weise, wie JavaScript die im Prototyp definierten Eigenschaften anfügt. Der tatsächliche Weg hängt mit dem Konzept der Prototypenkette zusammen.


ein. & b. Anstelle dieser beiden Schritte können Sie genau das gleiche Ergebnis erzielen, indem Sie Folgendes ausführen:

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"

jetzt können wir die Funktion getName in unserem my_person_prototype aufrufen:

newObject.getName();

c. dann gibt es das Objekt an den Konstruktor,

das können wir mit unserer probe machen wie:

Person.call(newObject, "George");

oder

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

dann kann der Konstruktor tun, was er will, weil dieses innerhalb dieses Konstruktors das gerade erstellte Objekt ist.

nun das Endresultat bevor die anderen Schritte simuliert werden: Object {name: "George"}


Zusammenfassung:

Wenn Sie das Schlüsselwort new für eine Funktion verwenden, rufen Sie diese auf, und diese Funktion dient als Konstruktor. Wenn Sie also sagen:

new FunctionName()

JavaScript erstellt intern ein Objekt, ein leeres Hash, und dann gibt es das Objekt an den Konstruktor. Der Konstruktor kann dann tun, was er will, da dieses innerhalb dieses Konstruktors das Objekt ist, das das Objekt ist wurde gerade erstellt und gibt Ihnen dieses Objekt natürlich dann, wenn Sie die return-Anweisung in Ihrer Funktion nicht verwendet haben oder wenn Sie einen return undefined; am Ende Ihres Funktionskörpers eingefügt haben.

Wenn JavaScript eine Eigenschaft für ein Objekt sucht, sucht es sie zunächst für dieses Objekt. Und dann gibt es eine geheime Eigenschaft [[prototype]], wie wir sie normalerweise haben __proto__ und diese Eigenschaft wird von JavaScript als Nächstes betrachtet. Und wenn es durch das __proto__ schaut, hat es, soweit es wieder ein anderes JavaScript-Objekt ist, ein eigenes __proto__ Attribut geht auf und ab bis zu dem Punkt, an dem der nächste __proto__ null ist. Der Punkt ist das einzige Objekt in JavaScript, dessen __proto__ Attribut null ist. Object.prototype Objekt:

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

und so funktioniert die Vererbung in JavaScript.

The prototype chain

Mit anderen Worten, wenn Sie eine Prototyp-Eigenschaft für eine Funktion haben und eine neue aufrufen, nachdem JavaScript das neu erstellte Objekt auf Eigenschaften überprüft hat, wird der .prototype der Funktion überprüft, und es ist auch möglich, dass dieses Objekt einen solchen hat einen eigenen internen Prototyp. und so weiter.

173
Mehran Hatami

Mit prototype können Sie Klassen erstellen. Wenn Sie prototype nicht verwenden, wird es statisch.

Hier ist ein kurzes Beispiel.

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

In dem obigen Fall haben Sie einen statischen Funktionsaufruftest. Auf diese Funktion kann nur mit obj.test zugegriffen werden, wo Sie sich obj als Klasse vorstellen können.

wo wie im folgenden Code

function obj()
{
}

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

Das obj ist zu einer Klasse geworden, die jetzt instanziiert werden kann. Es gibt mehrere Instanzen von obj, die alle die Funktion test haben.

Das obige ist mein Verständnis. Ich mache es zu einem Community-Wiki, damit die Leute mich korrigieren können, wenn ich falsch liege.

73
Ramesh

Die sieben Koans des Prototyps

Als Ciro San nach tiefer Meditation den Mount Fire Fox hinabstieg, war sein Geist klar und friedlich.

Seine Hand war jedoch unruhig und packte an sich einen Pinsel und notierte die folgenden Notizen.


0) Zwei verschiedene Dinge können als "Prototyp" bezeichnet werden:

  • die Prototyp-Eigenschaft, wie in obj.prototype

  • die interne Eigenschaft des Prototyps, die als [[Prototype]]in ES5 bezeichnet wird.

    Es kann über die ES5 Object.getPrototypeOf() abgerufen werden.

    Firefox macht es über die __proto__-Eigenschaft als Erweiterung zugänglich. ES6 wird jetzt erwähnt einige optionale Anforderungen für __proto__.


1) Diese Konzepte existieren, um die Frage zu beantworten:

Wenn ich obj.property mache, wo sucht JS .property?

Die klassische Vererbung sollte sich intuitiv auf die Suche nach Eigenschaften auswirken.


2)

  • __proto__ wird für den Punkt .-Eigenschaftensuche wie in obj.property verwendet. 
  • .prototype ist nicht, das direkt für die Suche verwendet wird, nur indirekt, da __proto__ bei der Objekterstellung mit new festgelegt wird.

Suchreihenfolge ist:

  • obj Eigenschaften hinzugefügt mit obj.p = ... oder Object.defineProperty(obj, ...)
  • eigenschaften von obj.__proto__
  • eigenschaften von obj.__proto__.__proto__ und so weiter
  • wenn __proto__null ist, geben Sie undefined zurück.

Dies ist die sogenannte Prototypkette.

Sie können .-Lookup mit obj.hasOwnProperty('key') und Object.getOwnPropertyNames(f) vermeiden.


3) Es gibt zwei Möglichkeiten, obj.__proto__ einzustellen:

  • new:

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

    dann hat new gesetzt:

    f.__proto__ === F.prototype
    

    In diesem wird .prototype verwendet.

  • Object.create:

     f = Object.create(proto)
    

    sets:

    f.__proto__ === proto
    

4) Der Code:

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

Entspricht dem folgenden Diagramm:

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

Dieses Diagramm zeigt viele in der Sprache vordefinierte Objektknoten: null, Object, Object.prototype, Function und Function.prototype. In unseren 2 Codezeilen wurden nur f, F und F.prototype erstellt.


5).constructor kommt normalerweise von F.prototype durch die .-Suche:

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

Wenn wir f.constructor schreiben, führt JavaScript die .-Suche durch:

  • f hat keinen .constructor
  • f.__proto__ === F.prototype hat .constructor === F, also nimm es

Das Ergebnis f.constructor == F ist intuitiv korrekt, da F zum Konstruieren von f verwendet wird, z. Setzen Sie Felder, ähnlich wie in klassischen OOP Sprachen. 


6) Eine klassische Vererbungssyntax kann durch Manipulieren von Prototypketten erreicht werden.

ES6 fügt die Schlüsselwörter class und extends hinzu. Hierbei handelt es sich lediglich um Syntaxzucker für den bisher möglichen Prototypen-Manipulationswahnsinn.

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.
(new C(1)).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

Vereinfachtes Diagramm ohne alle vordefinierten Objekte:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype

Nachdem ich diesen Thread gelesen habe, bin ich mit der JavaScript Prototype Chain verwirrt. Dann habe ich diese Diagramme gefunden 

http://iwiki.readthedocs.org/de/latest/javascript/js_core.html#inheritance*[[protytype]]* and <code>prototype</code> property of function objects

es ist eine übersichtliche Darstellung der JavaScript-Vererbung nach Prototypkette

und 

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

Dieses enthält ein Beispiel mit Code und mehrere Nice-Diagramme.

die Prototypkette greift letztendlich auf Object.prototype zurück. 

die Prototypkette kann technisch beliebig erweitert werden, indem der Prototyp der Unterklasse einem Objekt der übergeordneten Klasse gleichgesetzt wird.

Ich hoffe, es ist auch hilfreich für Sie, um die JavaScript-Prototypkette zu verstehen.

65
rockXrock

Jedes Objekt hat eine interne Eigenschaft [[Prototyp]], die es mit einem anderen Objekt verknüpft:

object [[Prototype]] -> anotherObject

In traditionellem Javascript ist das verknüpfte Objekt die prototype-Eigenschaft einer Funktion:

object [[Prototype]] -> aFunction.prototype

In einigen Umgebungen wird [[Prototyp]] als __proto__ angezeigt:

anObject.__proto__ === anotherObject

Sie erstellen den Link [[Prototyp]], wenn Sie ein Objekt erstellen.

// (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

Diese Aussagen sind also gleichwertig:

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

Eine new-Anweisung zeigt das Linkziel (Object.prototype) selbst nicht an. Stattdessen wird das Ziel vom Konstruktor (Object) impliziert.

Merken:

  • Jedes Objekt hat einen Link [[Prototyp]], der manchmal als __proto__ angezeigt wird.
  • Jede Funktion hat eine prototype-Eigenschaft.
  • Mit new erstellte Objekte sind mit der prototype-Eigenschaft ihres Konstruktors verknüpft.
  • Wenn eine Funktion nie als Konstruktor verwendet wird, wird ihre prototype-Eigenschaft nicht verwendet.
  • Wenn Sie keinen Konstruktor benötigen, verwenden Sie Object.create anstelle von new.
37
sam

Dieser Artikel ist lang. Ich bin mir jedoch sicher, dass die meisten Ihrer Fragen zur "prototypischen" Natur der JavaScript-Vererbung geklärt werden. Und noch mehr. Bitte lesen Sie den vollständigen Artikel.

JavaScript hat grundsätzlich zwei Arten von Datentypen

  • Non objects
  • Objects

Non objects

Es folgen die Datentypen Non object

  • string
  • Zahl (einschließlich NaN und Infinity)
  • Boolesche Werte (wahr, falsch)
  • nicht definiert

Diese Datentypen geben Folgendes zurück, wenn Sie den Operator typeof verwenden

typeof"string literal" (oder eine Variable, die string literal enthält) === 'string'

typeof5 (oder ein beliebiges numerisches Literal oder eine Variable mit numerischem Literal oder NaN oder Infynity) === 'number'

typeoftrue (oder false oder eine Variable, die true oder false enthält) === 'boolean'

typeofndefined (oder eine undefinierte Variable oder eine Variable, die ndefined enthält) === 'undefined'

Die Datentypen string, number und boolean können als Objects und Non objects dargestellt werden werden als Objekte dargestellt, deren Typ immer === 'Objekt' ist. Wir werden darauf zurückkommen, sobald wir die Objektdatentypen verstanden haben.

Objects

Die Objektdatentypen können weiter in zwei Typen unterteilt werden

  1. Funktionstyp Objekte
  2. Objekte ohne Funktionstyp

Die Function type objects sind diejenigen, die den String 'function' mit dem Operator typeof zurückgeben. Alle benutzerdefinierten Funktionen und alle in JavaScript eingebauten Objekte, die mithilfe des Operators new neue Objekte erstellen können, fallen in diese Kategorie. Zum Beispiel.

  • Objekt
  • String
  • Nummer
  • Boolean
  • Array
  • Typisierte Arrays
  • RegExp
  • Funktion
  • Alle anderen eingebauten Objekte, die mithilfe des Operators new neue Objekte erstellen können
  • function _ ​​serDefinedFunction () {/ * benutzerdefinierter Code * /}

Also, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Function) === typeof (UserDefinedFunction) === 'function'

Alle Function type objects sind tatsächlich Instanzen des eingebauten JavaScript-Objekts Function (einschließlich des Function -Objekts, d. H. Es ist rekursiv definiert). Es ist, als ob diese Objekte folgendermaßen definiert wurden

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")

Wie bereits erwähnt, können die Function type objects mit dem new operator neue Objekte erstellen. Zum Beispiel für ein Objekt vom Typ Objekt, Zeichenfolge, Nummer, Boolesch, Array, RegExp Oder serDefinedFunction kann mit erstellt werden

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() 

Die so erstellten Objekte sind alle Objekte vom Typ Non Function und geben deren typeof === 'object' zurück. In all diesen Fällen kann das Objekt "a" mit dem Operator new keine Objekte mehr erstellen. Das Folgende ist also falsch

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

Das eingebaute Objekt Math ist typeof === 'object'. Daher kann ein neues Objekt vom Typ Math nicht von einem neuen Operator erstellt werden.

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

Beachten Sie auch, dass die Funktionen Objekt, Array und RegExp ein neues Objekt erstellen können, ohne den Operator ne zu verwenden. Die folgenden tun es jedoch nicht.

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'

Die benutzerdefinierten Funktionen sind Sonderfälle.

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

Da die Function type objects neue Objekte erstellen können, werden sie auch Constructors genannt.

Jedes Konstruktor/Funktion (ob eingebaut oder benutzerdefiniert) hat bei der automatischen Definition eine Eigenschaft namens "Prototyp", deren Wert standardmäßig als Objekt festgelegt ist. Dieses Objekt selbst hat eine Eigenschaft namens "Konstruktor", die standardmäßig auf den Konstruktor/Funktion verweist.

Zum Beispiel, wenn wir eine Funktion definieren

function UserDefinedFunction()
{
}

folgendes geschieht automatisch

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

Diese "prototype" -Eigenschaft ist nur in den Function type objects vorhanden (und niemals in Non Function type objects).

Dies liegt daran, dass wenn ein neues Objekt erstellt wird (mit dem neuen Operator), alle Eigenschaften und Methoden des aktuellen Prototypobjekts der Konstruktorfunktion übernommen werden, dh eininterner Verweisist erstellt in dem neu erstellten Objekt, das auf das Objekt verweist, auf das das aktuelle Prototypobjekt der Konstruktorfunktion verweist.

Diese "interne Referenz", die im Objekt zum Verweisen auf geerbte Eigenschaften erstellt wird, wird als Objektprototyp bezeichnet (verweist auf das Objekt, auf das der "Prototyp" des Konstruktors verweist). Eigentum, aber es ist anders). Für jedes Objekt (Funktion oder Nichtfunktion) kann dies mit der Object.getPrototypeOf () -Methode abgerufen werden. Mit dieser Methode kann man die Prototypkette eines Objekts verfolgen.

Außerdem hat jedes Objekt, das erstellt wird (Funktionstyp oder Typ ohne Funktion) eine Eigenschaft "Konstruktor", die von der geerbt wird Objekt, auf das durch die Prototypeigenschaft der Konstruktorfunktion verwiesen wird. Standardmäßig verweist diese "Konstruktor" -Eigenschaft auf die Konstruktorfunktion, die sie erstellt hat (wenn der Konstruktorfunktion Standard "Prototyp" nicht geändert wurde).

Für alle Function type objects ist die Konstruktorfunktion immer function Function () {}

Für Objekte vom Typ Non Function (z. B. Javascript Built in Math-Objekt) ist die Konstruktorfunktion die Funktion, die sie erstellt hat. Für Math Objekt ist es function Object () {}.

Das gesamte oben erläuterte Konzept kann ohne unterstützenden Code ein wenig entmutigend sein. Bitte gehen Sie den folgenden Code Zeile für Zeile durch, um das Konzept zu verstehen. Versuchen Sie es auszuführen, um ein besseres Verständnis zu haben.

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"

Die Prototypkette jedes Objekts geht letztendlich auf Object.prototype zurück (das selbst kein Prototypobjekt hat). Der folgende Code kann zum Verfolgen der Prototypkette eines Objekts verwendet werden

var o=Starting object;

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

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

Die Prototypenkette für verschiedene Objekte funktioniert wie folgt.

  • Jedes Function-Objekt (einschließlich des eingebauten Function-Objekts) -> Function.prototype -> Object.prototype -> null
  • Einfache Objekte (erstellt von new Object () oder {} einschließlich integriertem Math-Objekt) -> Object.prototype -> null
  • Objekt erstellt mit new oder Object.create -> Eine oder mehrere Prototypketten -> Object.prototype -> null

Verwenden Sie zum Erstellen eines Objekts ohne Prototyp Folgendes:

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

Man könnte meinen, dass das Setzen der Prototypeigenschaft des Konstruktors auf Null ein Objekt mit einem Null-Prototyp erzeugt. In solchen Fällen wird der Prototyp des neu erstellten Objekts jedoch auf Object.prototype und der Konstruktor auf function Object festgelegt. Dies wird durch den folgenden Code veranschaulicht

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

Im Anschluss an die Zusammenfassung dieses Artikels

  • Es gibt zwei Arten von Objekten Funktionstypen und Nichtfunktionstypen
  • Nur Funktionstyp Objekte kann ein neues Objekt mit dem Operator ne erstellen. Die so erzeugten Objekte sind Nicht-Funktionstyp Objekte. Die Objekte vom Typ ohne Funktion können mit operator new kein Objekt mehr erstellen.

  • Alle Funktionstyp-Objekte haben standardmäßig eine "Prototyp" -Eigenschaft. Diese "prototype" -Eigenschaft verweist auf ein Objekt mit einer "constructor" -Eigenschaft, die standardmäßig auf das Function type object selbst verweist.

  • Alle Objekte (Funktionstyp und Nicht-Funktionstyp) haben eine "Konstruktor" -Eigenschaft, die standardmäßig auf das Objekt FunktionstypKonstruktor verweist. das hat es geschaffen.

  • Jedes Objekt, das intern erstellt wird, verweist auf das Objekt, auf das die Eigenschaft "prototype" des Konstruktors verweist, der es erstellt hat. Dieses Objekt ist als der erstellte _/object's prototype bekannt (der sich von der "prototype" -Eigenschaft des Funktionstypobjekts unterscheidet, auf die es verweist). Auf diese Weise kann das erstellte Objekt direkt auf die Methoden und Eigenschaften zugreifen, die in dem Objekt definiert sind, auf das durch die "prototype" -Eigenschaft des Konstruktors verwiesen wird (zum Zeitpunkt der Objekterstellung).

  • Mit der Methode Object.getPrototypeOf () kann ein Objektprototyp (und damit der Name seiner geerbten Eigenschaft) abgerufen werden. Tatsächlich kann diese Methode zum Navigieren in der gesamten Prototypkette des Objekts verwendet werden.

  • Die Prototypkette jedes Objekts geht letztendlich auf Object.prototype zurück (es sei denn, das Objekt wird mit Object.create (null) erstellt. In diesem Fall hat das Objekt keinen Prototyp).

  • typeof (new Array ()) === 'object' ist sprachlich bedingt und kein Fehler, auf den Douglas Crockford hingewiesen hat

  • Wenn Sie die Prototypeigenschaft des Konstruktors auf null setzen (oder undefiniert, Zahl, wahr, falsch, Zeichenfolge), wird kein Objekt mit einem Null-Prototyp erstellt. In solchen Fällen wird der Prototyp des neu erstellten Objekts auf Object.prototype und der Konstruktor auf function Object gesetzt.

Hoffe das hilft.

26
Arup Hore

Javascript hat keine Vererbung im üblichen Sinne, aber es hat die Prototypkette.

prototypkette

Wenn ein Element eines Objekts nicht im Objekt gefunden werden kann, sucht es in der Prototypkette danach. Die Kette besteht aus anderen Objekten. Auf den Prototyp einer Instanz kann mit der Variable __proto__ zugegriffen werden. Jedes Objekt hat ein Objekt, da in Javascript kein Unterschied zwischen Klassen und Instanzen besteht.

Das Hinzufügen einer Funktion/Variablen zum Prototyp hat den Vorteil, dass sie sich nur einmal im Speicher befinden muss und nicht für jede Instanz.

Es ist auch nützlich für die Vererbung, da die Prototypkette aus vielen anderen Objekten bestehen kann.

25
Georg Schölly

Das Konzept der prototypal - Vererbung ist für viele Entwickler eines der kompliziertesten. Versuchen wir, die Wurzel des Problems zu verstehen, um prototypal inheritance Besser zu verstehen. Beginnen wir mit einer Funktion plain.

enter image description here

Wenn wir einen new - Operator für den Tree function - Operator verwenden, nennen wir ihn eine constructor - Funktion.

enter image description here

Jede Funktion JavaScript hat eine prototype. Wenn Sie den Tree.prototype Protokollieren, erhalten Sie ...

enter image description here

Wenn Sie sich die obige Ausgabe von console.log() ansehen, sehen Sie möglicherweise auch eine Konstruktoreigenschaft für Tree.prototype Und eine __proto__ - Eigenschaft. Der __proto__ Repräsentiert den prototype, auf dem dieser function basiert, und da dies nur ein einfacher JavaScript function Ist, der nicht inheritance eingerichtet ist es bezieht sich jedoch auf den Object prototype, der nur in JavaScript integriert ist ...

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

Das hat Dinge wie .toString, .toValue, .hasOwnProperty Etc ...

__proto__, Das mein Mozilla mitgebracht hat, ist veraltet und wird durch die Methode Object.getPrototypeOf Ersetzt, um den object's prototype Zu erhalten.

enter image description here

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

Fügen wir unserer Treeprototype eine Methode hinzu.

enter image description here

Wir haben die Root geändert und eine function - Verzweigung hinzugefügt.

enter image description here

Wenn Sie also eine instance von Tree erstellen, können Sie die Methode branch aufrufen.

enter image description here

Wir können auch primitives oder objects zu unserer Prototype hinzufügen.

enter image description here

Fügen wir unserer Tree einen child-tree Hinzu.

enter image description here

Hier erbt die Child ihre prototype von Tree. Wir verwenden hier die Object.create() -Methode, um ein neues Objekt zu erstellen, basierend auf dem, was Sie übergeben, hier ist es Tree.prototype. In diesem Fall setzen wir den Prototyp von Child auf ein neues Objekt, das identisch mit dem Prototyp Tree aussieht. Als nächstes setzen wir den Child's constructor to Child, Wenn wir dies nicht tun, zeigt er auf Tree().

enter image description here

Child hat jetzt ein eigenes prototype, dessen __proto__ Auf Tree und dessen Tree's prototype Auf Basis Object zeigt.

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

Jetzt erstellen Sie ein instance von Child und rufen branch auf, das ursprünglich in Tree verfügbar war. Wir haben unser branch im Child prototype Nicht definiert. ABER in dem Root prototype, Von dem Child erbt.

enter image description here

In JS ist alles kein Objekt, alles kann sich wie ein Objekt verhalten.

Javascript hat Primitive wie strings, number, booleans, undefined, null. Sie sind keine object(i.e reference types), können sich aber durchaus wie ein object verhalten. Schauen wir uns hier ein Beispiel an.

enter image description here

In der ersten Zeile dieser Auflistung wird dem Namen ein Zeichenfolgenwert primitive zugewiesen. Die zweite Zeile behandelt den Namen wie ein object und ruft charAt(0) in Punktnotation auf.

Das passiert hinter den Kulissen: // was die JavaScript - Engine macht

enter image description here

Der String object Existiert nur für eine Anweisung, bevor er zerstört wird (ein Prozess mit dem Namen autoboxing). Kehren wir noch einmal zu unserer prototypalinheritance zurück.

  • Javascript unterstützt die Vererbung über delegation basierend auf prototypes.
  • Jedes Function hat eine prototype - Eigenschaft, die auf ein anderes Objekt verweist.
  • properties/functions Wird von der object selbst oder über die prototype - Kette gesucht, wenn sie nicht existiert

Ein prototype in JS ist ein Objekt, das Sie yields zum übergeordneten Objekt eines anderen object macht. [dh .. Delegation]Delegation bedeutet, dass Sie, wenn Sie nicht in der Lage sind, etwas zu tun, jemand anderem anweisen, es für Sie zu tun.

enter image description here

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

Wenn Sie die obige Fiedel nachschlagen, hat Hund Zugriff auf die Methode toString, diese steht jedoch nicht in der Methode zur Verfügung, sondern über die Prototypenkette, die an Object.prototype Delegiert.

enter image description here

Wenn Sie sich die folgende anschauen, versuchen wir, auf die Methode call zuzugreifen, die in jeder function verfügbar ist.

enter image description here

https://jsfiddle.net/rknffckc/

Wenn Sie die obige Fiedel nachschlagen, hat die Funktion Profile Zugriff auf die Methode call, ist jedoch nicht in ihr verfügbar, sondern über die Prototypkette, die an Function.prototype Delegiert.

enter image description here

Hinweis:prototype ist eine Eigenschaft des Funktionskonstruktors, wohingegen __proto__ Eine Eigenschaft der aus dem Funktionskonstruktor erstellten Objekte ist. Jede Funktion verfügt über eine prototype - Eigenschaft, deren Wert leer ist (object). Wenn wir eine Instanz der Funktion erstellen, erhalten wir eine interne Eigenschaft [[Prototype]] Oder __proto__, Deren Referenz der Prototyp der Funktion constructor ist.

enter image description here

Das obige Diagramm sieht etwas kompliziert aus, zeigt jedoch das ganze Bild der Funktionsweise von prototype chaining. Lass uns das langsam durchgehen:

Es gibt zwei Instanzen b1 Und b2, Deren Konstruktor Bar und übergeordnetes Element Foo ist und die zwei Methoden aus der Prototypkette identify und speak über Bar und Foo

enter image description here

https://jsfiddle.net/kbp7jr7n/

Wenn Sie den obigen Code nachschlagen, haben wir den Konstruktor Foo mit der Methode identify() und den Konstruktor Bar mit der Methode speak. Wir erstellen zwei Instanzen Barb1 Und b2, Deren übergeordneter Typ Foo ist. Während wir nun die speak - Methode von Bar aufrufen, können wir über die prototype - Kette identifizieren, wer das Sprechen anruft.

enter image description here

Bar verfügt nun über alle Methoden von Foo, die in prototype definiert sind. Lassen Sie uns die Zusammenhänge von Object.prototype Und Function.prototype Näher erläutern. Wenn Sie den Konstruktor von Foo nachschlagen, sind Bar und ObjectFunction constructor.

enter image description here

Das prototype von Bar ist Foo, prototype von Foo ist Object und wenn Sie genau hinsehen, das prototype von Foo ist verwandt mit Object.prototype.

enter image description here

Bevor wir dies schließen, lassen Sie uns hier einen kleinen Code einfügen, um alles oben zusammenzufassen. Wir verwenden hier den Operator instanceof, um zu überprüfen, ob ein object in seiner prototype - Kette die Eigenschaft prototype eines constructor hat, die im Folgenden zusammengefasst wird das gesamte große Diagramm.

enter image description here

Ich hoffe, dass dieses Add einige Informationen enthält, ich weiß, dass dies ein bisschen zu verstehen sein könnte ... in einfachen Worten, es ist es sind nur Objekte, die mit Objekten verknüpft sind !!!!

22
Thalaivar

was ist der genaue Zweck dieser Eigenschaft ".prototype"?

Die Schnittstelle zu Standardklassen wird erweiterbar. Beispielsweise verwenden Sie die Array-Klasse, und Sie müssen auch einen benutzerdefinierten Serializer für alle Array-Objekte hinzufügen. Möchten Sie eine Unterklasse codieren, eine Komposition verwenden oder ... Die Eigenschaft prototype löst dies, indem die Benutzer die für eine Klasse verfügbaren Elemente/Methoden genau steuern können.

Stellen Sie sich Prototypen als zusätzlichen vtable-Pointer vor. Wenn einige Mitglieder der ursprünglichen Klasse fehlen, wird der Prototyp zur Laufzeit nachgeschlagen.

20
dirkgently

Es kann hilfreich sein, Prototypketten in zwei Kategorien einzuordnen.

Betrachten Sie den Konstruktor:

 function Person() {}

Der Wert von Object.getPrototypeOf(Person) ist eine Funktion. Tatsächlich ist es Function.prototype. Da Person als Funktion erstellt wurde, hat es dasselbe Prototyp-Funktionsobjekt, das alle Funktionen besitzen. Es ist dasselbe wie Person.__proto__, aber diese Eigenschaft sollte nicht verwendet werden. Wie auch immer, mit Object.getPrototypeOf(Person) geht man effektiv die Leiter der sogenannten Prototyp-Kette hoch.

Die Kette nach oben sieht so aus:

PersonFunction.prototypeObject.prototype (Endpunkt)

Wichtig ist, dass diese Prototypkette wenig mit den Objekten zu tun hat, die Personconstruct kann. Diese konstruierten Objekte haben ihre eigene Prototypkette, und diese Kette kann möglicherweise keinen nahen Vorfahren mit dem oben genannten gemeinsam haben.

Nehmen Sie zum Beispiel dieses Objekt:

var p = new Person();

p hat keine direkte Prototypkettenbeziehung zu Person. Ihre Beziehung ist eine andere. Das Objekt p hat eine eigene Prototypkette. Wenn Sie Object.getPrototypeOf verwenden, wird die Kette wie folgt aussehen:

pPerson.prototypeObject.prototype (Endpunkt)

Es gibt kein Funktionsobjekt in dieser Kette (obwohl das sein könnte).

Person scheint also mit zwei Arten von Ketten verbunden zu sein, die ihr eigenes Leben leben. Um von einer Kette zur anderen zu "springen", verwenden Sie:

  1. .prototype: Springt von der Kette des Konstruktors zur Kette des erstellten Objekts. Diese Eigenschaft ist daher nur für Funktionsobjekte definiert (da new nur für Funktionen verwendet werden kann).

  2. .constructor: Springt von der Kette des erstellten Objekts zur Kette des Konstruktors.

Hier ist eine visuelle Darstellung der beiden beteiligten Prototypketten, dargestellt als Spalten:

 enter image description here

Zusammenfassen:

Die prototype-Eigenschaft gibt keine Informationen über die Prototypkette des subject, sondern über Objekte an, die von dem Subject erstellt wurden. 

Es ist keine Überraschung, dass der Name der Eigenschaft prototype zu Verwirrung führen kann. Es wäre vielleicht klarer gewesen, wenn diese Eigenschaft prototypeOfConstructedInstances oder etwas in dieser Richtung heißen würde.

Sie können zwischen den beiden Prototypketten hin und her springen:

Person.prototype.constructor === Person

Diese Symmetrie kann gebrochen werden, indem der prototype-Eigenschaft explizit ein anderes Objekt zugewiesen wird (dazu später mehr).

Erstellen Sie eine Funktion, Holen Sie sich zwei Objekte

Person.prototype ist ein Objekt, das zur gleichen Zeit erstellt wurde, zu der die Funktion Person erstellt wurde. Es hat Person als Konstruktor, obwohl dieser Konstruktor noch nicht ausgeführt wurde. So werden zwei Objekte gleichzeitig erstellt:

  1. Die Funktion Person selbst
  2. Das Objekt, das als Prototyp fungiert, wenn die Funktion als Konstruktor aufgerufen wird

Beide sind Objekte, die jedoch unterschiedliche Rollen haben: Das Funktionsobjekt constructs, während das andere Objekt den Prototyp jedes Objekts darstellt, das von der Funktion erstellt wird. Das Prototypobjekt wird zum übergeordneten Element des konstruierten Objekts in seiner Prototypkette.

Da eine Funktion auch ein Objekt ist, hat sie auch ein eigenes übergeordnetes Element in einer eigenen Prototypkette. Denken Sie jedoch daran, dass es sich bei diesen beiden Ketten um unterschiedliche Dinge handelt. 

Hier sind einige Gleichungen, die helfen könnten, das Problem zu verstehen - all diese Drucke 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);

Hinzufügen von Ebenen zur Prototypkette

Obwohl beim Erstellen einer Konstruktorfunktion ein Prototypobjekt erstellt wird, können Sie dieses Objekt ignorieren und ein anderes Objekt zuweisen, das als Prototyp für alle nachfolgenden Instanzen verwendet werden soll, die von diesem Konstruktor erstellt werden.

Zum Beispiel:

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

Nun ist die Prototypkette von t eine Stufe länger als die von p:

tpPerson.prototypeObject.prototype (Endpunkt)

Die andere Prototypkette ist nicht länger: Thief und Person sind Geschwister, die dasselbe Elternteil in ihrer Prototypkette haben:

Person}
Thief} → Function.prototypeObject.prototype (Endpunkt)

Die zuvor dargestellte Grafik kann dann auf diese erweitert werden (der ursprüngliche Thief.prototype wird ausgelassen):

 enter image description here

Die blauen Linien repräsentieren Prototypketten, die anderen farbigen Linien repräsentieren andere Beziehungen:

  • zwischen einem Objekt und seinem Konstruktor
  • zwischen einem Konstruktor und dem Prototypobjekt, das zum Erstellen von Objekten verwendet wird
19
trincot

Der definitive Leitfaden für objektorientiertes JavaScript - eine sehr kurze und klare 30-minütige Videoerklärung der gestellten Frage (Thema Prototypische Vererbung beginnt ab 5:45 , obwohl ich mir das ganze Video lieber anhören möchte ). Der Autor dieses Videos hat auch JavaScript object visualizer website http://www.objectplayground.com/ . erstellt enter image description here  enter image description here

16
Bad

Ich fand es hilfreich, die "Prototypkette" als rekursive Konvention zu erklären, wenn auf obj_n.prop_X verwiesen wird:

wenn obj_n.prop_X nicht vorhanden ist, überprüfen Sie obj_n+1.prop_X, wobei obj_n+1 = obj_n.[[prototype]]

Wenn der prop_X schließlich im k-ten Prototypobjekt gefunden wird, dann

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

Eine grafische Darstellung der Beziehung von Javascript-Objekten nach ihren Eigenschaften finden Sie hier:

js objects graph

http://jsobjects.org

14
B M

Wenn ein Konstruktor ein Objekt erstellt, verweist dieses Objekt implizit auf die Prototyp-Eigenschaft des Konstruktors, um Eigenschaftsverweise aufzulösen. Die Prototyp-Eigenschaft des Konstruktors kann durch den Programmausdruck constructor.prototype referenziert werden, und die zum Prototyp eines Objekts hinzugefügten Eigenschaften werden durch Vererbung von allen Objekten gemeinsam genutzt, die den Prototyp gemeinsam nutzen.

13
Tom

Es gibt zwei verschiedene, aber verwandte Entitäten, die erklärt werden müssen:

  • Die Eigenschaft .prototype von Funktionen.
  • Das [[Prototype]][1] Eigentum aller Objekte[2].

Das sind zwei verschiedene Dinge.

Die Eigenschaft [[Prototype]]:

Dies ist eine Eigenschaft, die für alle vorhanden ist[2] Objekte.

Was hier gespeichert wird, ist ein anderes Objekt, das als Objekt selbst ein eigenes [[Prototype]] hat, das auf ein anderes Objekt verweist. Dieses andere Objekt hat ein eigenes [[Prototype]]. Diese Geschichte wird fortgesetzt, bis Sie das prototypische Objekt erreichen, das Methoden bereitstellt, auf die für alle Objekte zugegriffen werden kann (wie .toString ).

Die [[Prototype]] -Eigenschaft ist Teil der [[Prototype]] -Kette. Diese Kette von [[Prototype]] Objekten wird untersucht, wenn beispielsweise [[Get]] oder [[Set]] Operationen an einem Objekt ausgeführt werden:

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

Die Eigenschaft .prototype:

Dies ist eine Eigenschaft, die nur für Funktionen verfügbar ist. Verwenden einer sehr einfachen Funktion:

function Bar(){};

Die Eigenschaft .prototype enthält ein Objekt , das b.[[Prototype]] zugewiesen wird, wenn Sie var b = new Bar. Sie können dies leicht überprüfen:

// 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

Eines der wichtigsten .prototype s ist das der Object -Funktion . Dieser Prototyp enthält das prototypische Objekt, das alle [[Prototype]] Ketten enthalten. Darauf sind alle verfügbaren Methoden für neue Objekte definiert:

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

Da .prototype ein Objekt ist, hat es jetzt eine [[Prototype]] -Eigenschaft. Wenn Sie Function.prototype keine Zuweisungen vornehmen, zeigt das .prototype des [[Prototype]] auf das prototypische Objekt (Object.prototype). Dies wird immer dann automatisch ausgeführt, wenn Sie eine neue Funktion erstellen.

Auf diese Weise erhalten Sie jedes Mal, wenn Sie new Bar; die Prototypenkette für Sie einrichten, alles, was in Bar.prototype definiert ist, und alles, was in Object.prototype definiert ist:

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)

Wenn Sie Function.prototype zuweisen , erweitern Sie die Prototypenkette nur um ein weiteres Objekt. Es ist wie eine Einfügung in eine einfach verknüpfte Liste.

Dies ändert im Wesentlichen die Kette [[Prototype]], sodass Eigenschaften, die für das Objekt definiert sind, das Function.prototype zugewiesen ist, von jedem von der Funktion erstellten Objekt angezeigt werden.


[1: Das wird niemanden verwirren; In vielen Implementierungen über die Eigenschaft __proto__ verfügbar gemacht.
[2]: Alle außer null .

Lassen Sie mich Ihnen mein Verständnis von Prototypen erklären. Ich werde die Vererbung hier nicht mit anderen Sprachen vergleichen. Ich wünschte, die Leute würden aufhören, Sprachen zu vergleichen, und die Sprache einfach als sich selbst verstehen. Prototypen und prototypische Vererbung zu verstehen, ist so einfach, wie ich Ihnen unten zeigen werde.

Prototyp ist wie ein Modell, auf dessen Grundlage Sie ein Produkt erstellen. Der entscheidende Punkt zu verstehen ist, dass, wenn Sie ein Objekt mit einem anderen Objekt als Prototyp erstellen, die Verbindung zwischen dem Prototyp und dem Produkt immer bestehen bleibt. Zum Beispiel:

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

Jedes Objekt enthält eine interne Eigenschaft namens [[Prototyp]], auf die mit der Funktion Object.getPrototypeOf() zugegriffen werden kann. Object.create(model) erstellt ein neues Objekt und legt seine Eigenschaft [[prototype]] auf object model fest. Wenn Sie also Object.getPrototypeOf(product) ausführen, erhalten Sie das Objekt model .

Eigenschaften im Produkt werden folgendermaßen behandelt:

  • Wenn auf eine Eigenschaft zugegriffen wird, um nur ihren Wert zu lesen, wird in der Scope-Chain nachgeschlagen. Die Suche nach der Variablen beginnt beim product bis zu ihrem Prototyp. Wenn eine solche Variable in der Suche gefunden wird, wird die Suche direkt dort gestoppt und der Wert wird zurückgegeben. Wenn eine solche Variable nicht in der Gültigkeitsbereichskette gefunden werden kann, wird undefined zurückgegeben.
  • Wenn eine Eigenschaft geschrieben (geändert) wird, wird die Eigenschaft immer auf das product object geschrieben. Wenn das product noch keine solche Eigenschaft hat, wird es implizit erstellt und geschrieben.

Eine solche Verknüpfung von Objekten unter Verwendung der Prototypeneigenschaft wird als prototypische Vererbung bezeichnet. Da ist es so einfach, stimme zu?

10
Aravind

Betrachten Sie das folgende keyValueStore-Objekt:

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; }
    };
})();

Ich kann eine neue Instanz dieses Objekts erstellen, indem Sie Folgendes tun:

kvs = keyValueStore.create();

Jede Instanz dieses Objekts hätte die folgenden öffentlichen Eigenschaften:

  • data
  • get 
  • set
  • delete
  • getLength

Nehmen wir an, wir erstellen 100 Instanzen dieses keyValueStore-Objekts. Obwohl get, set, delete, getLength für jede dieser 100 Instanzen dasselbe tun, hat jede Instanz eine eigene Kopie dieser Funktion.

Stellen Sie sich vor, Sie könnten nur eine einzige get-, set-, delete- und getLength-Kopie haben, und jede Instanz würde auf dieselbe Funktion verweisen. Dies ist besser für die Leistung und erfordert weniger Speicher.

Hier kommen Prototypen ins Spiel. Ein Prototyp ist ein "Bauplan" von Eigenschaften, der vererbt wird, aber nicht von Instanzen kopiert wird. Dies bedeutet, dass es nur einmal im Speicher für alle Instanzen eines Objekts vorhanden ist und von allen diesen Instanzen gemeinsam genutzt wird.

Betrachten Sie nun erneut das Objekt keyValueStore. Ich könnte es so umschreiben:

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; }
    };
})();

Dies entspricht genau der vorherigen Version des keyValueStore-Objekts, mit der Ausnahme, dass alle Methoden jetzt in einem Prototyp gespeichert sind. Dies bedeutet, dass alle 100 Instanzen nun diese vier Methoden verwenden, anstatt jeweils eine eigene Kopie zu besitzen.

9
John Slegers

Ein weiterer Versuch, JavaScript-Prototyp-basierte Vererbung mit besseren Bildern zu erklären

 Simple objects inheritanse

8
rus1

Ich mag immer Analogien, wenn es darum geht, solche Sachen zu verstehen. 'Prototypische Vererbung' ist meiner Meinung nach im Vergleich zur Klassenbass-Vererbung ziemlich verwirrend, obwohl Prototypen ein viel einfacheres Paradigma darstellen. Tatsächlich gibt es bei Prototypen keine Vererbung, daher ist der Name an und für sich irreführend, sondern eher eine Art „Delegation“.

Stell dir das vor ....

Du bist in der Highschool, und du bist in der Klasse und hast ein Quiz, das heute fällig ist, aber du hast keinen Stift, um deine Antworten auszufüllen. Doh!

Du sitzt neben deinem Freund Finnius, der vielleicht einen Stift hat. Sie fragen, und er sieht sich erfolglos an seinem Schreibtisch um, aber anstatt zu sagen "Ich habe keinen Stift", ist er ein netter Freund, den er mit seinem anderen Freund Derp abfragt, ob er einen Stift hat. Derp hat in der Tat einen Ersatzstift und gibt ihn an Finnius zurück, der ihn an Sie weitergibt, um Ihr Quiz abzuschließen. Derp hat den Stift Finnius anvertraut, der den Stift an Sie zur Verwendung delegiert hat.

Was hier wichtig ist, ist, dass Derp Ihnen den Stift nicht gibt, da Sie kein direktes Verhältnis zu ihm haben.

Dies ist ein vereinfachtes Beispiel für die Funktionsweise von Prototypen, bei denen ein Datenbaum nach dem gesuchten Objekt durchsucht wird.

7
Louis Moore

Zusammenfassung:

  • Funktionen sind Objekte in Javascript und können daher Eigenschaften haben
  • (Konstruktor-) Funktionen always haben eine Prototypeneigenschaft
  • Wenn eine Funktion als Konstruktor mit dem Schlüsselwort new verwendet wird, erhält das Objekt eine __proto__-Eigenschaft
  • Diese __proto__-Eigenschaft verweist auf die prototype-Eigenschaft der Konstruktorfunktion.

Beispiel:

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.

Warum ist das nützlich?

Javascript hat einen Mechanismus beim Nachschlagen von Eigenschaften in Objekten, der als 'prototypal Vererbung' bezeichnet wird.

  • Zuerst wird geprüft, ob sich die Eigenschaft im Objekt selbst befindet. Wenn ja, wird diese Eigenschaft zurückgegeben.
  • Befindet sich die Immobilie nicht auf dem Objekt selbst, klettert sie die Protochain hoch. Es betrachtet im Wesentlichen das Objekt, auf das die proto - Eigenschaft verweist. Dort wird geprüft, ob die Eigenschaft für das Objekt verfügbar ist, auf das mit proto verwiesen wird.
  • Wenn sich die Eigenschaft nicht im proto -Objekt befindet, steigt sie die proto - Kette bis zum Object-Objekt an.
  • Wenn es die Eigenschaft nirgendwo auf dem Objekt und seiner Prototypkette finden kann, wird es undefined zurückgegeben.

Zum Beispiel:

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

let mySelf = new Person('Willem');

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

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

4

ein anderes Schema zeigt die Beziehungen _PROTO_, prototype und constructor: enter image description here

3
IvanM

Es ist nur so, dass Sie bereits ein Objekt mit Object.new haben, aber Sie haben immer noch kein Objekt, wenn Sie die Konstruktorsyntax verwenden.

2
shiva kumar

Der Prototyp erstellt neues Objekt durch Klonen des vorhandenen Objekts. Wenn wir also über Prototyp nachdenken, können wir wirklich denken klonen oder eine Kopie von etwas machen, anstatt es zu erfinden.

2
Arif

Es ist wichtig zu verstehen, dass zwischen dem Prototyp eines Objekts (der über Object.getPrototypeOf(obj) oder über die nicht mehr unterstützte obj.__proto__-Eigenschaft verfügbar ist) und der Prototyp-Eigenschaft für Konstruktorfunktionen unterschieden wird. Ersteres ist die Eigenschaft in jeder Instanz, und letzteres ist die Eigenschaft im Konstruktor. Das heißt, Object.getPrototypeOf(new Foobar()) bezieht sich auf dasselbe Objekt wie Foobar.prototype.

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

0
Baraa Al-Tabbaa