JavaScript-Objekt erstellen - So definieren Sie Objekte in JS

Objekte sind die Haupteinheit der Kapselung in der objektorientierten Programmierung. In diesem Artikel werde ich verschiedene Möglichkeiten zum Erstellen von Objekten in JavaScript beschreiben. Sie sind:

  • Objektliteral
  • Object.create ()
  • Klassen
  • Werksfunktionen

Objektliteral

Zunächst müssen wir zwischen Datenstrukturen und objektorientierten Objekten unterscheiden. Datenstrukturen haben öffentliche Daten und kein Verhalten. Das heißt, sie haben keine Methoden.

Wir können solche Objekte einfach mit der Objektliteral-Syntax erstellen. Es sieht aus wie das:

const product = { name: 'apple', category: 'fruits', price: 1.99 } console.log(product);

Objekte in JavaScript sind dynamische Sammlungen von Schlüssel-Wert-Paaren. Der Schlüssel ist immer eine Zeichenfolge und muss in der Sammlung eindeutig sein. Der Wert kann ein Grundelement, ein Objekt oder sogar eine Funktion sein.

Wir können auf eine Eigenschaft mit der Punkt- oder Quadratnotation zugreifen.

console.log(product.name); //"apple" console.log(product["name"]); //"apple"

Hier ist ein Beispiel, in dem der Wert ein anderes Objekt ist.

const product = { name: 'apple', category: 'fruits', price: 1.99, nutrients : { carbs: 0.95, fats: 0.3, protein: 0.2 } }

Der Wert der carbsEigenschaft ist ein neues Objekt. So können wir auf die carbsImmobilie zugreifen .

console.log(product.nutrients.carbs); //0.95

Abkürzung Eigenschaftsnamen

Stellen Sie sich den Fall vor, in dem die Werte unserer Eigenschaften in Variablen gespeichert sind.

const name = 'apple'; const category = 'fruits'; const price = 1.99; const product = { name: name, category: category, price: price }

JavaScript unterstützt sogenannte Shorthand-Eigenschaftsnamen. Es ermöglicht uns, ein Objekt nur mit dem Namen der Variablen zu erstellen. Es wird eine Eigenschaft mit demselben Namen erstellt. Das nächste Objektliteral entspricht dem vorherigen.

const name = 'apple'; const category = 'fruits'; const price = 1.99; const product = { name, category, price }

Object.create

Als nächstes schauen wir uns an, wie Objekte mit verhaltensorientierten Objekten implementiert werden.

JavaScript verfügt über ein sogenanntes Prototypsystem, mit dem das Verhalten von Objekten gemeinsam genutzt werden kann. Die Hauptidee besteht darin, ein Objekt namens Prototyp mit einem gemeinsamen Verhalten zu erstellen und es dann beim Erstellen neuer Objekte zu verwenden.

Mit dem Prototypsystem können wir Objekte erstellen, die das Verhalten von anderen Objekten erben.

Erstellen wir ein Prototypobjekt, mit dem wir Produkte hinzufügen und den Gesamtpreis aus einem Warenkorb abrufen können.

const cartPrototype = { addProduct: function(product){ if(!this.products){ this.products = [product] } else { this.products.push(product); } }, getTotalPrice: function(){ return this.products.reduce((total, p) => total + p.price, 0); } }

Beachten Sie, dass diesmal der Wert der Eigenschaft addProducteine Funktion ist. Wir können das vorherige Objekt auch mit einer kürzeren Form schreiben, die als Kurzmethoden-Syntax bezeichnet wird.

const cartPrototype = { addProduct(product){/*code*/}, getTotalPrice(){/*code*/} }

Das cartPrototypeist das Prototypobjekt , das das allgemeine Verhalten von zwei Verfahren dargestellt hält, addProductund getTotalPrice. Es kann verwendet werden, um andere Objekte zu erstellen, die dieses Verhalten erben.

const cart = Object.create(cartPrototype); cart.addProduct({name: 'orange', price: 1.25}); cart.addProduct({name: 'lemon', price: 1.75}); console.log(cart.getTotalPrice()); //3

Das cartObjekt hat cartPrototypeals Prototyp. Es erbt das Verhalten davon. carthat eine versteckte Eigenschaft, die auf das Prototypobjekt verweist.

Wenn wir eine Methode für ein Objekt verwenden, wird diese Methode zuerst für das Objekt selbst und nicht für dessen Prototyp gesucht.

Dies

Beachten Sie, dass wir ein spezielles Schlüsselwort verwenden, das aufgerufen wird this, um auf die Daten des Objekts zuzugreifen und diese zu ändern.

Denken Sie daran, dass Funktionen in JavaScript unabhängige Verhaltenseinheiten sind. Sie sind nicht unbedingt Teil eines Objekts. Wenn dies der Fall ist, benötigen wir eine Referenz, mit der die Funktion auf andere Mitglieder desselben Objekts zugreifen kann. thisist der Funktionskontext. Es bietet Zugriff auf andere Eigenschaften.

Daten

Sie fragen sich vielleicht, warum wir die productsEigenschaft für das Prototypobjekt selbst nicht definiert und initialisiert haben .

Das sollten wir nicht tun. Prototypen sollten verwendet werden, um Verhalten zu teilen, nicht Daten. Das Teilen von Daten führt dazu, dass dieselben Produkte auf mehreren Warenkorbobjekten vorhanden sind. Betrachten Sie den folgenden Code:

const cartPrototype = { products:[], addProduct: function(product){ this.products.push(product); }, getTotalPrice: function(){} } const cart1 = Object.create(cartPrototype); cart1.addProduct({name: 'orange', price: 1.25}); cart1.addProduct({name: 'lemon', price: 1.75}); console.log(cart1.getTotalPrice()); //3 const cart2 = Object.create(cartPrototype); console.log(cart2.getTotalPrice()); //3

Sowohl die cart1als auch die cart2Objekte, die das gemeinsame Verhalten von erben, cartPrototypeteilen dieselben Daten. Das wollen wir nicht. Prototypen sollten verwendet werden, um Verhalten zu teilen, nicht Daten.

Klasse

Das Prototypensystem ist keine übliche Methode zum Erstellen von Objekten. Entwickler sind besser mit dem Erstellen von Objekten aus Klassen vertraut.

Die Klassensyntax ermöglicht eine vertraute Methode zum Erstellen von Objekten mit einem gemeinsamen Verhalten. Es wird immer noch derselbe Prototyp hinter den Kulissen erstellt, aber die Syntax ist klarer und wir vermeiden auch das vorherige datenbezogene Problem. Die Klasse bietet einen bestimmten Ort, um die für jedes Objekt unterschiedlichen Daten zu definieren.

Hier ist dasselbe Objekt, das mit der Klassenzuckersyntax erstellt wurde:

class Cart{ constructor(){ this.products = []; } addProduct(product){ this.products.push(product); } getTotalPrice(){ return this.products.reduce((total, p) => total + p.price, 0); } } const cart = new Cart(); cart.addProduct({name: 'orange', price: 1.25}); cart.addProduct({name: 'lemon', price: 1.75}); console.log(cart.getTotalPrice()); //3 const cart2 = new Cart(); console.log(cart2.getTotalPrice()); //0

Notice that the class has a constructor method that initialized that data distinct for each new object. The data in the constructor is not shared between instances. In order to create a new instance, we use the new keyword.

I think the class syntax is more clear and familiar to most developers. Nevertheless, it does a similar thing, it creates a prototype with all the methods and uses it to define new objects. The prototype can be accessed with Cart.prototype.

It turns out that the prototype system is flexible enough to allow the class syntax. So the class system can be simulated using the prototype system.

Private Properties

The only thing is that the products property on the new object is public by default.

console.log(cart.products); //[{name: "orange", price: 1.25} // {name: "lemon", price: 1.75}]

We can make it private using the hash # prefix.

Private properties are declared with #name syntax. # is a part of the property name itself and should be used for declaring and accessing the property. Here is an example of declaring products as a private property:

class Cart{ #products constructor(){ this.#products = []; } addProduct(product){ this.#products.push(product); } getTotalPrice(){ return this.#products.reduce((total, p) => total + p.price, 0); } } console.log(cart.#products); //Uncaught SyntaxError: Private field '#products' must be declared in an enclosing class

Factory Functions

Another option is to create objects as collections of closures.

Closure is the ability of a function to access variables and parameters from the other function even after the outer function has executed. Take a look at the cart object built with what is called a factory function.

function Cart() { const products = []; function addProduct(product){ products.push(product); } function getTotalPrice(){ return products.reduce((total, p) => total + p.price, 0); } return { addProduct, getTotalPrice } } const cart = Cart(); cart.addProduct({name: 'orange', price: 1.25}); cart.addProduct({name: 'lemon', price: 1.75}); console.log(cart.getTotalPrice()); //3

addProduct and getTotalPrice are two inner functions accessing the variable products from their parent. They have access to the products variable event after the parent Cart has executed. addProduct and getTotalPrice are two closures sharing the same private variable.

Cart is a factory function.

The new object cart created with the factory function has the products variable private. It cannot be accessed from the outside.

console.log(cart.products); //undefined

Factory functions don’t need the new keyword but you can use it if you want. It will return the same object no matter if you use it or not.

Recap

Usually, we work with two types of objects, data structures that have public data and no behavior and object-oriented objects that have private data and public behavior.

Data structures can be easily built using the object literal syntax.

JavaScript offers two innovative ways of creating object-oriented objects. The first is using a prototype object to share the common behavior. Objects inherit from other objects. Classes offer a nice sugar syntax to create such objects.

The other option is to define objects are collections of closures.

For more on closures and function programming techniques check out my book series Functional Programming with JavaScript and React.

The Functional Programming in JavaScript book is coming out.