Objektorientierte Programmierung in JavaScript - Erklärt anhand von Beispielen

JavaScript ist keine klassenbasierte objektorientierte Sprache. Es gibt jedoch immer noch Möglichkeiten zur Verwendung der objektorientierten Programmierung (OOP).

In diesem Tutorial erkläre ich OOP und zeige Ihnen, wie man es benutzt.

Klassenbasierte Programmierung ist laut Wikipedia

Ein Stil der objektorientierten Programmierung (OOP), bei dem die Vererbung über das Definieren von Objektklassen erfolgt, anstatt dass die Vererbung nur über die Objekte erfolgt

Das beliebteste Modell von OOP ist klassenbasiert.

Wie bereits erwähnt, handelt es sich bei JavaScript nicht um eine klassenbasierte Sprache, sondern um eine prototypbasierte Sprache.

Nach Mozillas Dokumentation:

Eine prototypbasierte Sprache hat den Begriff eines prototypischen Objekts, eines Objekts, das als Vorlage verwendet wird, um die anfänglichen Eigenschaften für ein neues Objekt abzurufen.

Schauen Sie sich diesen Code an:

let names = { fname: "Dillion", lname: "Megida" } console.log(names.fname); console.log(names.hasOwnProperty("mname")); // Expected Output // Dillion // false 

Die Objektvariable nameshat nur zwei Eigenschaften - fnameund lname. Überhaupt keine Methoden.

Woher kommt also hasOwnProperty?

Nun, es kommt vom ObjectPrototyp.

Versuchen Sie, den Inhalt der Variablen in der Konsole zu protokollieren:

console.log(names); 

Wenn Sie die Ergebnisse in der Konsole erweitern, erhalten Sie Folgendes:

Beachten Sie die letzte Eigenschaft - __proto__? Versuchen Sie es zu erweitern:

Unter dem ObjectKonstruktor wird eine Reihe von Eigenschaften angezeigt. Alle diese Eigenschaften stammen vom globalen ObjectPrototyp. Wenn Sie genau hinschauen, werden Sie auch unser verstecktes bemerken hasOwnProperty.

Mit anderen Worten, alle Objekte haben Zugriff auf den ObjectPrototyp des Objekts . Sie besitzen diese Eigenschaften nicht, erhalten jedoch Zugriff auf die Eigenschaften des Prototyps.

Das __proto__Eigentum

Dies zeigt auf das Objekt, das als Prototyp verwendet wird.

Dies ist die Eigenschaft für jedes Objekt, das ihm Zugriff auf die Object prototypeEigenschaft gewährt .

Jedes Objekt verfügt standardmäßig über diese Eigenschaft, die sich auf die bezieht, Object Protoypesofern nichts anderes konfiguriert ist (dh wenn das Objekt __proto__auf einen anderen Prototyp zeigt).

Ändern der __proto__Eigenschaft

Diese Eigenschaft kann geändert werden, indem explizit angegeben wird, dass sie sich auf einen anderen Prototyp beziehen soll. Die folgenden Methoden werden verwendet, um dies zu erreichen:

Object.create()

function DogObject(name, age) { let dog = Object.create(constructorObject); dog.name = name; dog.age = age; return dog; } let constructorObject = { speak: function(){ return "I am a dog" } } let bingo = DogObject("Bingo", 54); console.log(bingo); 

In der Konsole haben Sie Folgendes:

Beachten Sie die __proto__Eigenschaft und die speakMethode?

Object.create verwendet das übergebene Argument, um zum Prototyp zu werden.

new Stichwort

function DogObject(name, age) { this.name = name; this.age = age; } DogObject.prototype.speak = function() { return "I am a dog"; } let john = new DogObject("John", 45); 

johnDie __proto__Eigenschaft von ist auf DogObjectden Prototyp gerichtet. Denken Sie jedoch daran, dass DogObjectder Prototyp ein Objekt ist ( Schlüssel- und Wertepaar ), daher hat er auch eine __proto__Eigenschaft, die sich auf den globalen ObjectPrototyp bezieht .

Diese Technik wird als PROTOTYPENKETTE bezeichnet .

Beachten Sie Folgendes: Der newKeyword-Ansatz macht dasselbe wie Object.create(), macht es jedoch nur einfacher, da er einige Dinge automatisch für Sie erledigt.

Und so...

Jedes Objekt in Javascript hat Objectstandardmäßig Zugriff auf den Prototyp des Objekts . Bei entsprechender Konfiguration einen weiteren Prototyp zu verwenden, sagen wir prototype2, dann prototype2hätte auch Zugriff auf den Prototyp des Objekts standardmäßig und so weiter.

Objekt + Funktionskombination

Sie sind wahrscheinlich verwirrt über die Tatsache, dass DogObjectes sich um eine Funktion ( function DogObject(){}) handelt, auf deren Eigenschaften mit einer Punktnotation zugegriffen wird . Dies wird als Funktionsobjektkombination bezeichnet .

Wenn Funktionen deklariert werden, erhalten sie standardmäßig viele damit verbundene Eigenschaften. Denken Sie daran, dass Funktionen auch Objekte in JavaScript-Datentypen sind.

Nun, Klasse

JavaScript hat das classSchlüsselwort in ECMAScript 2015 eingeführt. Dadurch wirkt JavaScript wie eine OOP-Sprache. Aber es ist nur syntatischer Zucker gegenüber der bestehenden Prototyping-Technik. Es setzt sein Prototyping im Hintergrund fort, lässt aber den äußeren Körper wie OOP aussehen. Wir werden uns nun ansehen, wie das möglich ist.

Das folgende Beispiel ist eine allgemeine Verwendung von a classin JavaScript:

class Animals { constructor(name, specie) { this.name = name; this.specie = specie; } sing() { return `${this.name} can sing`; } dance() { return `${this.name} can dance`; } } let bingo = new Animals("Bingo", "Hairy"); console.log(bingo); 

Dies ist das Ergebnis in der Konsole:

The __proto__ references the Animals prototype (which in turn references the Object prototype).

From this, we can see that the constructor defines the major features while everything outside the constructor (sing() and dance()) are the bonus features (prototypes).

In the background, using the new keyword approach, the above translates to:

function Animals(name, specie) { this.name = name; this.specie = specie; } Animals.prototype.sing = function(){ return `${this.name} can sing`; } Animals.prototype.dance = function() { return `${this.name} can dance`; } let Bingo = new Animals("Bingo", "Hairy"); 

Subclassing

This is a feature in OOP where a class inherits features from a parent class but possesses extra features which the parent doesn't.

The idea here is, for example, say you want to create a cats class. Instead of creating the class from scratch - stating the name, age and species property afresh, you'd inherit those properties from the parent animals class.

This cats class can then have extra properties like color of whiskers.

Let's see how subclasses are done with class.

Here, we need a parent which the subclass inherits from. Examine the following code:

class Animals { constructor(name, age) { this.name = name; this.age = age; } sing() { return `${this.name} can sing`; } dance() { return `${this.name} can dance`; } } class Cats extends Animals { constructor(name, age, whiskerColor) { super(name, age); this.whiskerColor = whiskerColor; } whiskers() { return `I have ${this.whiskerColor} whiskers`; } } let clara = new Cats("Clara", 33, "indigo"); 

With the above, we get the following outputs:

console.log(clara.sing()); console.log(clara.whiskers()); // Expected Output // "Clara can sing" // "I have indigo whiskers" 

When you log the contents of clara out in the console, we have:

You'll notice that clara has a __proto__ property which references the constructor Cats and gets access to the whiskers() method. This __proto__ property also has a __proto__ property which references the constructor Animals thereby getting access to sing() and dance(). name and age are properties that exist on every object created from this.

Using the Object.create method approach, the above translates to:

function Animals(name, age) { let newAnimal = Object.create(animalConstructor); newAnimal.name = name; newAnimal.age = age; return newAnimal; } let animalConstructor = { sing: function() { return `${this.name} can sing`; }, dance: function() { return `${this.name} can dance`; } } function Cats(name, age, whiskerColor) { let newCat = Animals(name, age); Object.setPrototypeOf(newCat, catConstructor); newCat.whiskerColor = whiskerColor; return newCat; } let catConstructor = { whiskers() { return `I have ${this.whiskerColor} whiskers`; } } Object.setPrototypeOf(catConstructor, animalConstructor); const clara = Cats("Clara", 33, "purple"); clara.sing(); clara.whiskers(); // Expected Output // "Clara can sing" // "I have purple whiskers" 

Object.setPrototypeOf is a method which takes in two arguments - the object (first argument) and the desired prototype (second argument).

From the above, the Animals function returns an object with the animalConstructor as prototype. The Cats function returns an object with catConstructor as it's prototype. catConstructor on the other hand, is given a prototype of animalConstructor.

Therefore, ordinary animals only have access to the animalConstructor but cats have access to the catConstructor and the animalConstructor.

Wrapping Up

JavaScript leverages its prototype nature to welcome OOP developers to its ecosystem. It also provides easy ways to creating prototypes and organize related data.

True OOP languages do not perform prototyping in the background - just take note of that.

A big thanks to Will Sentance's course on Frontend Masters - JavaScript: The Hard Parts of Object Oriented JavaScript. I learned everything you see in this article (plus a little extra research) from his course. You should check it out.

You can hit me up on Twitter at iamdillion for any questions or contributions.

Thanks for reading : )

Useful Resources

  • Object-oriented JavaScript for beginners
  • Introduction to Object Oriented Programming in JavaScript