Der folgende Beitrag ist ein Gastbeitrag von Faraz Kelhini. Einige dieser Dinge liegen außerhalb meiner Komfortzone, also bat ich Kyle Simpson, ihn für mich technisch zu prüfen. Kyles Antwort (die wir während einer Office Hours-Sitzung gaben) war sehr interessant. Sie lautete: 1) Dieser Artikel ist technisch einwandfrei. JavaScript hat keine Klassen im herkömmlichen Sinne und dies ist der Weg, wie die meisten Leute sie hineinpressen. 2) Wir sollten aufhören, sie hineinzupressen. JavaScript hat Objekte und wir können sie so verwenden, wie sie gedacht sind, um die gleichen Dinge zu tun. Kyle nennt es OLOO (Objects Linked to Other Objects). Hier ist eine Einführung. Ich denke, es gibt Wert darin, beides zu lernen.
Ein gutes Verständnis von Konstruktoren ist entscheidend, um die JavaScript-Sprache wirklich zu verstehen. Technisch gesehen hat JavaScript keine Klassen, aber es hat Konstruktoren und Prototypen, um ähnliche Funktionalitäten in JavaScript zu bringen. Tatsächlich fungiert die Klassen Deklaration, die in ES2015 eingeführt wurde, einfach als syntaktischer Zucker über der bestehenden Prototypen-basierten Vererbung und fügt der Sprache keine zusätzliche Funktionalität hinzu.
In diesem Tutorial werden wir Konstruktoren im Detail untersuchen und sehen, wie JavaScript sie zur Erstellung von Objekten nutzt.
Konstruktoren erstellen und verwenden
Konstruktoren sind wie normale Funktionen, aber wir verwenden sie mit dem Schlüsselwort new. Es gibt zwei Arten von Konstruktoren: eingebaute Konstruktoren wie Array und Object, die zur Laufzeit automatisch in der Ausführungsumgebung verfügbar sind; und benutzerdefinierte Konstruktoren, die Eigenschaften und Methoden für Ihren eigenen Objekttyp definieren.
Ein Konstruktor ist nützlich, wenn Sie mehrere ähnliche Objekte mit denselben Eigenschaften und Methoden erstellen möchten. Es ist üblich, den Namen von Konstruktoren großzuschreiben, um sie von normalen Funktionen zu unterscheiden. Betrachten Sie den folgenden Code
function Book() {
// unfinished code
}
var myBook = new Book();
Die letzte Zeile des Codes erstellt eine Instanz von Book und weist sie einer Variablen zu. Obwohl der Book-Konstruktor nichts tut, ist myBook trotzdem eine Instanz davon. Wie Sie sehen können, gibt es keinen Unterschied zwischen dieser Funktion und normalen Funktionen, außer dass sie mit dem Schlüsselwort new aufgerufen wird und der Funktionsname großgeschrieben ist.
Bestimmen des Typs einer Instanz
Um herauszufinden, ob ein Objekt eine Instanz eines anderen ist, verwenden wir den Operator instanceof
myBook instanceof Book // true
myBook instanceof String // false
Beachten Sie, dass dies einen Fehler auslöst, wenn die rechte Seite des instanceof-Operators keine Funktion ist
myBook instanceof {};
// TypeError: invalid 'instanceof' operand ({})
Eine weitere Möglichkeit, den Typ einer Instanz zu ermitteln, ist die Verwendung der Eigenschaft constructor. Betrachten Sie das folgende Codefragment
myBook.constructor === Book; // true
Die constructor-Eigenschaft von myBook verweist auf Book, daher gibt der strikte Gleichheitsoperator true zurück. Jedes Objekt in JavaScript erbt eine constructor-Eigenschaft von seinem Prototypen, die auf die Konstruktorfunktion verweist, die das Objekt erstellt hat
var s = new String("text");
s.constructor === String; // true
"text".constructor === String; // true
var o = new Object();
o.constructor === Object; // true
var o = {};
o.constructor === Object; // true
var a = new Array();
a.constructor === Array; // true
[].constructor === Array; // true
Beachten Sie jedoch, dass die Verwendung der constructor-Eigenschaft zur Überprüfung des Typs einer Instanz im Allgemeinen als schlechte Praxis angesehen wird, da sie überschrieben werden kann.
Benutzerdefinierte Konstruktorfunktionen
Ein Konstruktor ist wie ein Ausstecher zum Erstellen mehrerer Objekte mit denselben Eigenschaften und Methoden. Betrachten Sie das folgende Beispiel
function Book(name, year) {
this.name = name;
this.year = '(' + year + ')';
}
Der Book-Konstruktor erwartet zwei Parameter: name und year. Wenn der Konstruktor mit dem Schlüsselwort new aufgerufen wird, weist er die empfangenen Parameter der name- und year-Eigenschaft der aktuellen Instanz zu, wie unten gezeigt
var firstBook = new Book("Pro AngularJS", 2014);
var secondBook = new Book("Secrets Of The JavaScript Ninja", 2013);
var thirdBook = new Book("JavaScript Patterns", 2010);
console.log(firstBook.name, firstBook.year);
console.log(secondBook.name, secondBook.year);
console.log(thirdBook.name, thirdBook.year);
Dieser Code gibt Folgendes auf der Konsole aus

Wie Sie sehen, können wir schnell eine große Anzahl verschiedener Buchobjekte erstellen, indem wir den Book-Konstruktor mit verschiedenen Argumenten aufrufen. Dies ist genau das gleiche Muster, das JavaScript in seinen eingebauten Konstruktoren wie Array() und Date() verwendet.
Die Methode Object.defineProperty()
Die Methode Object.defineProperty() kann innerhalb eines Konstruktors verwendet werden, um alle notwendigen Eigenschafteneinrichtungen durchzuführen. Betrachten Sie den folgenden Konstruktor
function Book(name) {
Object.defineProperty(this, "name", {
get: function() {
return "Book: " + name;
},
set: function(newName) {
name = newName;
},
configurable: false
});
}
var myBook = new Book("Single Page Web Applications");
console.log(myBook.name); // Book: Single Page Web Applications
// we cannot delete the name property because "configurable" is set to false
delete myBook.name;
console.log(myBook.name); // Book: Single Page Web Applications
// but we can change the value of the name property
myBook.name = "Testable JavaScript";
console.log(myBook.name); // Book: Testable JavaScript
Dieser Code verwendet Object.defineProperty(), um Accessor-Eigenschaften zu definieren. Accessor-Eigenschaften enthalten keine Eigenschaften oder Methoden, aber sie definieren einen Getter, der aufgerufen wird, wenn die Eigenschaft gelesen wird, und einen Setter, der aufgerufen wird, wenn die Eigenschaft geschrieben wird.
Ein Getter soll einen Wert zurückgeben, während ein Setter den Wert, der der Eigenschaft zugewiesen wird, als Argument erhält. Der obige Konstruktor gibt eine Instanz zurück, deren name-Eigenschaft festgelegt oder geändert werden kann, aber nicht gelöscht werden kann. Wenn wir den Wert von name abrufen, fügt der Getter der Zeichenfolge Book: den Namen hinzu und gibt ihn zurück.
Objekt-Literal-Schreibweisen werden Konstruktoren vorgezogen
Die JavaScript-Sprache hat neun eingebaute Konstruktoren: Object(), Array(), String(), Number(), Boolean(), Date(), Function(), Error() und RegExp(). Beim Erstellen von Werten können wir entweder Objekt-Literale oder Konstruktoren verwenden. Objekt-Literale sind jedoch nicht nur leichter zu lesen, sondern auch schneller auszuführen, da sie zur Parse-Zeit optimiert werden können. Daher ist es am besten, sich bei einfachen Objekten an Literale zu halten
// a number object
// numbers have a toFixed() method
var obj = new Object(5);
obj.toFixed(2); // 5.00
// we can achieve the same result using literals
var num = 5;
num.toFixed(2); // 5.00
// a string object
// strings have a slice() method
var obj = new String("text");
obj.slice(0,2); // "te"
// same as above
var string = "text";
string.slice(0,2); // "te"
Wie Sie sehen, gibt es kaum einen Unterschied zwischen Objekt-Literalen und Konstruktoren. Interessanter ist, dass es immer noch möglich ist, Methoden auf Literalen aufzurufen. Wenn eine Methode auf einem Literal aufgerufen wird, konvertiert JavaScript das Literal automatisch in ein temporäres Objekt, damit die Methode die Operation ausführen kann. Sobald das temporäre Objekt nicht mehr benötigt wird, verwirft JavaScript es.
Die Verwendung des new-Schlüsselworts ist unerlässlich
Es ist wichtig zu bedenken, dass vor allen Konstruktoren das Schlüsselwort new verwendet werden muss. Wenn Sie new versehentlich vergessen, ändern Sie das globale Objekt anstelle des neu erstellten Objekts. Betrachten Sie das folgende Beispiel
function Book(name, year) {
console.log(this);
this.name = name;
this.year = year;
}
var myBook = Book("js book", 2014);
console.log(myBook instanceof Book);
console.log(window.name, window.year);
var myBook = new Book("js book", 2014);
console.log(myBook instanceof Book);
console.log(myBook.name, myBook.year);
Hier ist, was dieser Code auf der Konsole ausgibt

Wenn wir den Book-Konstruktor ohne new aufrufen, rufen wir tatsächlich eine Funktion ohne Rückgabeanweisung auf. Daher verweist this innerhalb des Konstruktors auf Window (anstelle von myBook), und es werden zwei globale Variablen erstellt. Wenn wir die Funktion jedoch mit new aufrufen, wird der Kontext von global (Window) auf die Instanz umgeschaltet. Daher verweist this korrekt auf myBook.
Beachten Sie, dass dieser Code im Strict Mode einen Fehler auslösen würde, da der Strict Mode dazu dient, den Programmierer vor versehentlichem Aufrufen eines Konstruktors ohne das Schlüsselwort new zu schützen.
Scope-sichere Konstruktoren
Wie wir gesehen haben, ist ein Konstruktor einfach eine Funktion, daher kann er ohne das Schlüsselwort new aufgerufen werden. Aber für unerfahrene Programmierer kann dies eine Fehlerquelle sein. Ein Scope-sicherer Konstruktor ist so konzipiert, dass er dasselbe Ergebnis liefert, unabhängig davon, ob er mit oder ohne new aufgerufen wird, und leidet daher nicht unter diesen Problemen.
Die meisten eingebauten Konstruktoren wie Object, Regex und Array sind Scope-sicher. Sie verwenden ein spezielles Muster, um zu bestimmen, wie der Konstruktor aufgerufen wird. Wenn new nicht verwendet wird, geben sie eine ordnungsgemäße Instanz des Objekts zurück, indem sie den Konstruktor erneut mit new aufrufen. Betrachten Sie den folgenden Code
function Fn(argument) {
// if "this" is not an instance of the constructor
// it means it was called without new
if (!(this instanceof Fn)) {
// call the constructor again with new
return new Fn(argument);
}
}
Eine Scope-sichere Version unseres Konstruktors würde also so aussehen
function Book(name, year) {
if (!(this instanceof Book)) {
return new Book(name, year);
}
this.name = name;
this.year = year;
}
var person1 = new Book("js book", 2014);
var person2 = Book("js book", 2014);
console.log(person1 instanceof Book); // true
console.log(person2 instanceof Book); // true
Fazit
Es ist wichtig zu verstehen, dass die Klassen Deklaration, die in ES2015 eingeführt wurde, einfach als syntaktischer Zucker über der bestehenden Prototypen-basierten Vererbung fungiert und nichts Neues zu JavaScript hinzufügt. Konstruktoren und Prototypen sind die primäre Methode von JavaScript, um ähnliche und verwandte Objekte zu definieren.
In diesem Artikel haben wir uns genau angesehen, wie JavaScript-Konstruktoren funktionieren. Wir haben gelernt, dass Konstruktoren wie normale Funktionen sind, aber mit dem Schlüsselwort new verwendet werden. Wir haben gesehen, wie Konstruktoren es uns ermöglichen, schnell mehrere ähnliche Objekte mit denselben Eigenschaften und Methoden zu erstellen, und warum der instanceof-Operator der sicherste Weg ist, den Typ einer Instanz zu bestimmen. Schließlich haben wir uns Scope-sichere Konstruktoren angesehen, die mit oder ohne new aufgerufen werden können.
Absolut! Das Prototypen-Entwurfsmuster schien mir nie wirklich zu liegen. DAMIT kann ich leben.
classist seit einiger Zeit ein reserviertes Wort, und ECMAScript 2015 nutzt es, um JavaScript eine Form der klassischen Vererbung zur Verfügung zu stellen.Browser unterstützen das
class-Schlüsselwort noch nicht, aber wenn Sie etwas wieESNextBabel verwenden, können Sie mit ES6-Features (ES2015) schreiben und in ES5-kompatibles JavaScript kompilieren.Ob das
class-Schlüsselwort tatsächlich ein „guter Teil“ ist, ist Gegenstand einiger Diskussionen. Ich habe noch nicht angefangen, ES2015-Features zu verwenden, um dazu eine Aussage treffen zu können.Dieser Artikel sollte einen Abschnitt über ES6 enthalten.
Klare, prägnante Erklärung. Ich würde noch hinzufügen, dass, wenn Sie mehrere neue Konstruktoren in einer Schleife aufrufen möchten (vielleicht aus einer Liste anderer Elemente), hier ein einfacher Weg ist, eine IIFE zu verwenden
Sie brauchen in Ihrem Beispiel keine IIFE. Das wird auch funktionieren
Vielleicht wollten Sie etwas anderes zeigen?
Sie können die schreckliche for-Schleife komplett streichen und die funktionale Seite von JS etwas mehr umarmen.
// Unsere bescheidene Konstruktorfunktion
function MyConstructor(name, order) {
this.name = name;
this.order = order;
}
// Initialisieren Sie alle unsere neuen Konstruktoren aus einem Array
var myConstructorArray = function(yourArrayOfWhatever) {
return yourArrayOfWhatever.map(function(i, value){
return new MyConstructor(‘item-‘ + (i), i);
});
};
console.log(myConstructorArray([1, 2, 3, 4, 5]));
@hkhjkhj: Wahr, die
map-Version ist definitiv viel sauberer. (Beachten Sie nur, dass sie vorwärts zählt, während das ursprüngliche Beispiel rückwärts zählt.)@Agop, um eine Map-Transformation umzukehren, können Sie einfach das Array umkehren und dann Map aufrufen. Zum Beispiel
Ich frage mich, wie man das mit Vererbung / Erweiterung von Objekten verwendet?
In Bezug auf die Vererbung ist
object.create()wahrscheinlich besser geeignet. Zum Erweitern von Objekten können Sie gemeinsame Eigenschaften zum Prototypen hinzufügen.Das ist das Einzige, was ich mir gewünscht hätte, dass dieser Artikel darauf eingeht (das und Prototypenketten).
Im Grunde würde man klassische JS-Vererbung so machen
Nicht das Schönste auf der Welt, aber es ergibt viel Sinn im Hinblick auf das prototypische Vererbungsmodell von JS.
Danke für die Vorschläge, Leute, ich werde es ausprobieren.
Ich war enttäuscht, dass dieser Artikel nicht auf ES6-Klassen einging
Ich wette, Sie könnten viel im Internet finden. Hier ist eine Seite auf MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Vielen Dank für diesen Artikel. Mir ist ein kleiner Tippfehler aufgefallen: Sollte der folgende Satz „Der Buchkonstruktor erwartet zwei Parameter: Name und Alter;“ nicht „Jahr“ am Ende statt „Alter“ angeben?
Gut bemerkt, ich habe das im Artikel korrigiert.
Ich bin kein Fan dieser Art, Objekte zu erstellen – mit Konstruktoren und ‚new‘. Es ist voller Probleme.
Das Factory-Pattern ist eine viel bessere Alternative, da jede Funktion ein Objekt erstellen und zurückgeben kann und nicht mit ‚new‘ aufgerufen werden muss.
Eric Elliot erklärt im Detail, warum Factories besser geeignet sind als ‚new‘-Konstruktoren =>
http://ericleads.com/2013/01/javascript-constructor-functions-vs-factory-functions/
Was ES6-Klassen betrifft – sie sind eine schlechte Ergänzung für die Sprache. Ich bin der Meinung, wenn man Klassen verwenden will, sollte man kein JavaScript schreiben.
Ich denke auch, dass Vererbung stark überstrapaziert wird und im modernen Rechnen braucht man sie nur, wenn man Zehntausende von Objekten erstellt.
Funktionskomposition ist fast immer vorteilhafter als Vererbung. Senior Spotify-Entwickler Mattias Johansson erklärt eloquent, warum dies der Fall ist, in diesem Video: => https://www.youtube.com/watch?v=wfMtDGfHWpA
Sie sollten zwischen der Eigenschaft eines Konstruktors und der Funktion eines Konstruktors unterscheiden.
Ich habe beschlossen, JavaScript-Unterricht zu nehmen. Anfangs war es verwirrend, da ich nur an Laienbegriffe gewöhnt war. Dank Ihres Beitrags habe ich definitiv die Informationen und Klarheit erhalten, die ich brauchte. Vielen Dank!