Während viele Leute hier sagen, dass es keinen besten Weg für die Objekterstellung gibt, gibt es eine Begründung dafür, warum es ab 2019 so viele Möglichkeiten gibt, Objekte in JavaScript zu erstellen, und dies hat mit dem Fortschritt von JavaScript über die verschiedenen Iterationen zu tun von EcmaScript-Veröffentlichungen aus dem Jahr 1997.
Vor ECMAScript 5 gab es nur zwei Möglichkeiten, Objekte zu erstellen: die Konstruktorfunktion oder die Literalnotation (eine bessere Alternative zu new Object ()). Mit der Konstruktorfunktionsnotation erstellen Sie ein Objekt, das in mehrere Instanzen instanziiert werden kann (mit dem neuen Schlüsselwort), während die Literalnotation ein einzelnes Objekt wie einen Singleton liefert.
// constructor function
function Person() {};
// literal notation
var Person = {};
Unabhängig von der von Ihnen verwendeten Methode sind JavaScript-Objekte einfach Eigenschaften von Schlüsselwertpaaren:
// Method 1: dot notation
obj.firstName = 'Bob';
// Method 2: bracket notation. With bracket notation, you can use invalid characters for a javascript identifier.
obj['lastName'] = 'Smith';
// Method 3: Object.defineProperty
Object.defineProperty(obj, 'firstName', {
value: 'Bob',
writable: true,
configurable: true,
enumerable: false
})
// Method 4: Object.defineProperties
Object.defineProperties(obj, {
firstName: {
value: 'Bob',
writable: true
},
lastName: {
value: 'Smith',
writable: false
}
});
In früheren Versionen von JavaScript bestand die einzige Möglichkeit, die klassenbasierte Vererbung nachzuahmen, in der Verwendung von Konstruktorfunktionen. Die Konstruktorfunktion ist eine spezielle Funktion, die mit dem Schlüsselwort 'new' aufgerufen wird. Konventionell wird die Funktionskennung großgeschrieben, albiet ist sie nicht erforderlich. Innerhalb des Konstruktors verweisen wir auf das Schlüsselwort 'this', um dem Objekt, das die Konstruktorfunktion implizit erstellt, Eigenschaften hinzuzufügen. Die Konstruktorfunktion gibt das neue Objekt mit den aufgefüllten Eigenschaften implizit implizit an die aufrufende Funktion zurück, es sei denn, Sie verwenden explizit das Schlüsselwort return und geben etwas anderes zurück.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
this.sayName = function(){
return "My name is " + this.firstName + " " + this.lastName;
}
}
var bob = new Person("Bob", "Smith");
bob instanceOf Person // true
Es liegt ein Problem mit der sayName-Methode vor. In objektorientierten klassenbasierten Programmiersprachen verwenden Sie normalerweise Klassen als Fabriken, um Objekte zu erstellen. Jedes Objekt verfügt über eigene Instanzvariablen, jedoch über einen Zeiger auf die im Klassenentwurf definierten Methoden. Wenn Sie die Konstruktorfunktion von JavaScript verwenden, wird bei jedem Aufruf eine neue sayName-Eigenschaft für das neu erstellte Objekt definiert. Jedes Objekt verfügt also über eine eigene Eigenschaft sayName. Dies verbraucht mehr Speicherressourcen.
Neben der Erhöhung der Speicherressourcen wird durch die Definition von Methoden innerhalb der Konstruktorfunktion die Möglichkeit der Vererbung ausgeschlossen. Auch hier wird die Methode als Eigenschaft für das neu erstellte Objekt und für kein anderes Objekt definiert, sodass die Vererbung nicht wie folgt funktionieren kann. Daher stellt JavaScript die Prototypenkette als eine Form der Vererbung bereit, wodurch JavaScript zu einer prototypischen Sprache wird.
Wenn Sie ein Elternteil haben und ein Elternteil viele Eigenschaften eines Kindes teilt, sollte das Kind diese Eigenschaften erben. Vor ES5 wurde dies wie folgt erreicht:
function Parent(eyeColor, hairColor) {
this.eyeColor = eyeColor;
this.hairColor = hairColor;
}
Parent.prototype.getEyeColor = function() {
console.log('has ' + this.eyeColor);
}
Parent.prototype.getHairColor = function() {
console.log('has ' + this.hairColor);
}
function Child(firstName, lastName) {
Parent.call(this, arguments[2], arguments[3]);
this.firstName = firstName;
this.lastName = lastName;
}
Child.prototype = Parent.prototype;
var child = new Child('Bob', 'Smith', 'blue', 'blonde');
child.getEyeColor(); // has blue eyes
child.getHairColor(); // has blonde hair
Die Art und Weise, wie wir die obige Prototypenkette verwendet haben, ist eigenartig. Da der Prototyp ein Live-Link ist, würden Sie durch Ändern der Eigenschaft eines Objekts in der Prototypkette auch dieselbe Eigenschaft eines anderen Objekts ändern. Das Ändern der geerbten Methode eines Kindes sollte natürlich nicht die Methode des Elternteils ändern. Object.create hat dieses Problem mithilfe einer Polyfüllung behoben. Mit Object.create können Sie also die Eigenschaft eines Kindes in der Prototypkette sicher ändern, ohne die gleiche Eigenschaft des Elternteils in der Prototypkette zu beeinflussen.
ECMAScript 5 führte Object.create ein, um den oben genannten Fehler in der Konstruktorfunktion für die Objekterstellung zu beheben. Die Object.create () -Methode ERSTELLT ein neues Objekt, wobei ein vorhandenes Objekt als Prototyp des neu erstellten Objekts verwendet wird. Da ein neues Objekt erstellt wird, besteht kein Problem mehr, bei dem durch Ändern der untergeordneten Eigenschaft in der Prototypkette der Verweis des übergeordneten Elements auf diese Eigenschaft in der Kette geändert wird.
var bobSmith = {
firstName: "Bob",
lastName: "Smith",
sayName: function(){
return "My name is " + this.firstName + " " + this.lastName;
}
}
var janeSmith = Object.create(bobSmith, {
firstName : { value: "Jane" }
})
console.log(bobSmith.sayName()); // My name is Bob Smith
console.log(janeSmith.sayName()); // My name is Jane Smith
janeSmith.__proto__ == bobSmith; // true
janeSmith instanceof bobSmith; // Uncaught TypeError: Right-hand side of 'instanceof' is not callable. Error occurs because bobSmith is not a constructor function.
Vor ES6 gab es hier ein allgemeines Erstellungsmuster für die Verwendung von Funktionskonstruktoren und Object.create:
const View = function(element){
this.element = element;
}
View.prototype = {
getElement: function(){
this.element
}
}
const SubView = function(element){
View.call(this, element);
}
SubView.prototype = Object.create(View.prototype);
Jetzt wurde Object.create in Verbindung mit Konstruktorfunktionen häufig für die Objekterstellung und -vererbung in JavaScript verwendet. ES6 führte jedoch das Konzept von Klassen ein, bei denen es sich in erster Linie um syntaktischen Zucker gegenüber der vorhandenen prototypbasierten Vererbung von JavaScript handelt. Die Klassensyntax führt kein neues objektorientiertes Vererbungsmodell in JavaScript ein. Somit bleibt JavaScript eine prototypische Sprache.
ES6-Klassen erleichtern die Vererbung erheblich. Wir müssen die Prototypfunktionen der übergeordneten Klasse nicht mehr manuell kopieren und den Konstruktor der untergeordneten Klasse zurücksetzen.
// create parent class
class Person {
constructor (name) {
this.name = name;
}
}
// create child class and extend our parent class
class Boy extends Person {
constructor (name, color) {
// invoke our parent constructor function passing in any required parameters
super(name);
this.favoriteColor = color;
}
}
const boy = new Boy('bob', 'blue')
boy.favoriteColor; // blue
Insgesamt fielen diese 5 verschiedenen Strategien der Objekterstellung in JavaScript mit der Entwicklung des EcmaScript-Standards zusammen.