JavaScript verwendet das prototypische Vererbungsmodell: Jedes Objekt erbt Felder (Eigenschaften) und Methoden des Prototypobjekts.
In Java oder Swift als Vorlagen oder Schemas zum Erstellen von Objekten verwendete Klassen sind in JavaScript nicht vorhanden. Es gibt nur Objekte in der prototypischen Vererbung.
Die prototypische Vererbung kann das klassische Modell der Klassenvererbung nachahmen. Zu diesem Zweck führte ES6 das Schlüsselwort class ein: syntaktischer Zucker für die prototypische Vererbung.
In diesem Artikel erfahren Sie, wie Sie mit Klassen arbeiten: Definieren Sie Klassen, ihre privaten (privaten) und öffentlichen (öffentlichen) Felder und Methoden und erstellen Sie Instanzen.
1. Definition: das Schlüsselwort class
Das Schlüsselwort class wird verwendet, um eine Klasse zu definieren:
class User {
//
}
Diese Syntax wird als Klassendeklaration bezeichnet.
Die Klasse hat möglicherweise keinen Namen. Mit einem Klassenausdruck können Sie einer Variablen eine Klasse zuweisen:
const UserClass = class {
//
}
Klassen können als Module exportiert werden. Hier ist ein Beispiel für den Standardexport:
export default class User {
//
}
Und hier ist ein Beispiel für einen benannten Export:
export class User {
//
}
Klassen werden zum Erstellen von Instanzen verwendet. Eine Instanz ist ein Objekt, das die Daten und die Logik einer Klasse enthält.
Instanzen werden mit dem Operator new: instance = new Class () erstellt.
So erstellen Sie eine Instanz der Benutzerklasse:
const myUser = new User()
2. Initialisierung: Konstruktor ()
Der Konstruktor (param1, param2, ...) ist eine spezielle Methode innerhalb einer Klasse, die eine Instanz initialisiert. Hier werden die Anfangswerte für die Instanzfelder festgelegt und konfiguriert.
Im folgenden Beispiel legt der Konstruktor den Anfangswert für das Namensfeld fest:
class User {
constructor(name) {
this.name = name
}
}
Der Konstruktor verwendet einen Parameter, name, mit dem der Anfangswert des Felds this.name festgelegt wird.
Dies im Konstruktor zeigt auf die Instanz, die erstellt wird.
Das zur Instanziierung der Klasse verwendete Argument wird zu einem Parameter für den Konstruktor:
class User {
constructor(name) {
name //
this.name = name
}
}
const user = new User('')
Der Parameter name im Konstruktor hat den Wert 'Pechorin'.
Wenn Sie keinen eigenen Konstruktor definieren, wird ein Standardkonstruktor erstellt. Dies ist eine leere Funktion, die die Instanz nicht beeinflusst.
3. Felder
Klassenfelder sind Variablen, die bestimmte Informationen enthalten. Die Felder können in zwei Gruppen unterteilt werden:
- Klasseninstanzfelder
- Felder der Klasse selbst (statisch)
Die Felder haben auch zwei Zugriffsebenen:
- Öffentlich (öffentlich): Felder sind sowohl innerhalb der Klasse als auch in Instanzen verfügbar
- Privat (privat): Felder sind nur innerhalb der Klasse zugänglich
3.1. Öffentliche Felder von Klasseninstanzen
class User {
constructor(name) {
this.name = name
}
}
Der Ausdruck this.name = name erstellt einen Instanzfeldnamen und weist ihm einen Anfangswert zu.
Auf dieses Feld kann mit einem Eigenschafts-Accessor zugegriffen werden:
const user = new User('')
user.name //
In diesem Fall ist name ein öffentliches Feld, da außerhalb der User-Klasse darauf zugegriffen werden kann.
Beim impliziten Erstellen von Feldern in einem Konstruktor ist es schwierig, eine Liste aller Felder zu erhalten. Dazu müssen die Felder aus dem Konstruktor abgerufen werden.
Am besten definieren Sie die Felder der Klasse explizit. Es spielt keine Rolle, was der Konstruktor tut, die Instanz hat immer die gleichen Felder.
Mit dem Vorschlag zum Erstellen von Klassenfeldern können Sie Felder innerhalb einer Klasse definieren. Außerdem können Sie hier den Feldern Anfangswerte zuweisen:
class SomeClass {
field1
field2 = ' '
// ...
}
Ändern Sie den Code der Benutzerklasse, indem Sie darin ein öffentliches Namensfeld definieren:
class User {
name
constructor(name) {
this.name = name
}
}
const user = new User('')
user.name //
Diese öffentlichen Felder sind sehr beschreibend. Ein kurzer Blick auf die Klasse ermöglicht es Ihnen, ihre Datenstruktur zu verstehen.
Darüber hinaus kann ein Klassenfeld zum Zeitpunkt der Definition initialisiert werden:
class User {
name = ''
constructor() {
//
}
}
const user = new User()
user.name //
Es gibt keine Einschränkungen für den Zugriff auf offene Felder und deren Änderung. Sie können solche Felder im Konstruktor, in den Methoden und außerhalb der Klasse lesen und ihnen Werte zuweisen.
3.2. Private Felder von Klasseninstanzen
Mit der Kapselung können Sie die internen Implementierungsdetails einer Klasse ausblenden. Wer die gekapselte Klasse verwendet, verlässt sich auf die öffentliche Schnittstelle, ohne auf die Details der Implementierung der Klasse einzugehen.
Diese Klassen sind einfacher zu aktualisieren, wenn sich die Implementierungsdetails ändern.
Eine gute Möglichkeit, Details auszublenden, besteht darin, private Felder zu verwenden. Solche Felder können nur innerhalb der Klasse gelesen und geändert werden, zu der sie gehören. Private Felder sind außerhalb der Klasse nicht verfügbar.
Um ein Feld privat zu machen, müssen Sie seinem Namen ein # -Symbol voranstellen, z. B. #myPrivateField. Wenn Sie sich auf ein solches Feld beziehen, muss immer das angegebene Präfix verwendet werden.
Machen wir das Namensfeld privat:
class User {
#name
constructor(name) {
this.#name = name
}
getName() {
return this.#name
}
}
const user = new User('')
user.getName() //
user.#name // SyntaxError
#name ist ein privates Feld. Es kann nur innerhalb der User-Klasse zugegriffen werden. Die Methode getName () führt dies aus.
Der Versuch, auf #name außerhalb der User-Klasse zuzugreifen, führt jedoch zu einem Syntaxfehler: SyntaxError: Das private Feld '#name' muss in einer umschließenden Klasse deklariert werden.
3.3. Öffentliche statische Felder
In einer Klasse können Sie Felder definieren, die zur Klasse selbst gehören: statische Felder. Solche Felder werden verwendet, um Konstanten zu erstellen, die die Informationen speichern, die die Klasse benötigt.
Verwenden Sie zum Erstellen statischer Felder das Schlüsselwort static vor dem Feldnamen: static myStaticField.
Fügen wir ein neues Typfeld hinzu, um den Benutzertyp zu definieren: Administrator oder regulär. Die statischen Felder TYPE_ADMIN und TYPE_REGULAR sind Konstanten für jeden Benutzertyp:
class User {
static TYPE_ADMIN = 'admin'
static TYPE_REGULAR = 'regular'
name
type
constructor(name, type) {
this.name = name
this.type = type
}
}
const admin = new User(' ', User.TYPE_ADMIN)
admin.type === User.TYPE_ADMIN // true
Verwenden Sie für den Zugriff auf statische Felder den Klassennamen und den Eigenschaftsnamen: User.TYPE_ADMIN und User.TYPE_REGULAR.
3.4. Private statische Felder
Manchmal sind statische Felder auch Teil der internen Implementierung der Klasse. Um solche Felder zu kapseln, können Sie sie privat machen.
Stellen Sie dazu dem Feldnamen #: static #myPrivateStaticFiled voran.
Angenommen, wir möchten die Anzahl der Instanzen der Benutzerklasse begrenzen. Private statische Felder können erstellt werden, um Informationen über die Anzahl der Instanzen auszublenden:
class User {
static #MAX_INSTANCES = 2
static #instances = 0
}
name
constructor(name) {
User.#instances++
if (User.#instances > User.#MAX_INSTANCES) {
throw new Error(' User')
}
this.name = name
}
new User('')
new User('')
new User('') // User
Das statische Feld Benutzer. # MAX_INSTANCES definiert die zulässige Anzahl von Instanzen und Benutzer. # Instanzen definiert die Anzahl der erstellten Instanzen.
Diese privaten statischen Felder sind nur innerhalb der User-Klasse verfügbar. Nichts von der Außenwelt kann die Einschränkungen beeinflussen: Dies ist einer der Vorteile der Kapselung.
Ca. Lane: Wenn Sie die Anzahl der Instanzen auf eins beschränken, erhalten Sie eine interessante Implementierung des Singleton-Entwurfsmusters.
4. Methoden
Die Felder enthalten Daten. Die Möglichkeit, Daten zu ändern, wird durch spezielle Funktionen bereitgestellt, die Teil der Klasse sind: Methoden.
JavaScript unterstützt sowohl Instanzmethoden als auch statische Methoden.
4.1. Instanzmethoden
Methoden einer Instanz einer Klasse können ihre Daten ändern. Instanzmethoden können sowohl andere Instanzmethoden als auch statische Methoden aufrufen.
Definieren wir beispielsweise eine getName () -Methode, die den Benutzernamen zurückgibt:
class User {
name = ''
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
const user = new User('')
user.getName() //
In einer Klassenmethode sowie in einem Konstruktor zeigt dies auf die Instanz, die erstellt wird. Verwenden Sie diese Option, um Instanzdaten abzurufen: this.field oder um Methoden aufzurufen: this.method ().
Fügen wir eine neue Methode nameContains (str) hinzu, die ein Argument akzeptiert und eine andere Methode aufruft:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
nameContains(str) {
return this.getName().includes(str)
}
}
const user = new User('')
user.nameContains('') // true
user.nameContains('') // false
nameContains (str) ist eine Methode der User-Klasse, die ein Argument akzeptiert. Es ruft eine andere Instanzmethode getName () auf, um den Benutzernamen abzurufen.
Die Methode kann auch privat sein. Verwenden Sie das Präfix #, um eine Methode privat zu machen.
Machen wir die Methode getName () privat:
class User {
#name
constructor(name) {
this.#name = name
}
#getName() {
return this.#name
}
nameContains(str) {
return this.#getName().includes(str)
}
}
const user = new User('')
user.nameContains('') // true
user.nameContains('') // false
user.#getName // SyntaxError
#getName () ist eine private Methode. Innerhalb der nameContains (str) -Methode nennen wir es so. # GetName ().
Da die Methode #getName () privat ist, kann sie nicht außerhalb der User-Klasse aufgerufen werden.
4.2. Getter und Setter
Getter und Setter sind Accessoren oder berechnete Eigenschaften. Dies sind Methoden, die Felder imitieren, aber das Lesen und Schreiben von Daten ermöglichen.
Getter werden verwendet, um Daten zu empfangen, Setter werden verwendet, um sie zu ändern.
Um zu verhindern, dass dem Namensfeld eine leere Zeichenfolge zugewiesen wird, wickeln Sie das private Feld #nameValue in einen Getter und Setter ein:
class User {
#nameValue
constructor(name) {
this.name = name
}
get name() {
return this.#nameValue
}
set name(name) {
if (name === '') {
throw new Error(' ')
}
this.#nameValue = name
}
}
const user = new User('')
user.name // ,
user.name = '' //
user.name = '' //
4.3. Statische Methoden
Statische Methoden sind Funktionen, die zur Klasse selbst gehören. Sie definieren die Logik der Klasse, nicht ihre Instanzen.
Verwenden Sie zum Erstellen einer statischen Methode das Schlüsselwort static vor dem Methodennamen: static myStaticMethod ().
Bei der Arbeit mit statischen Methoden sind zwei einfache Regeln zu beachten:
- Eine statische Methode hat Zugriff auf statische Felder
- Es hat keinen Zugriff auf Instanzfelder
Erstellen wir eine statische Methode, um zu überprüfen, ob bereits ein Benutzer mit dem angegebenen Namen erstellt wurde:
class User {
static #takenNames = []
static isNameTaken(name) {
return User.#takenNames.includes(name)
}
name = ''
constructor(name) {
this.name = name
User.#takenNames.push(name)
}
}
const user = new User('')
User.isNameTaken('') // true
User.isNameTaken('') // false
isNameTaken () ist eine statische Methode, die das private statische Feld User. # takeNames verwendet, um zu bestimmen, welche Namen verwendet wurden.
Statische Methoden können auch privat sein: static #myPrivateStaticMethod (). Solche Methoden können nur innerhalb der Klasse aufgerufen werden.
5. Vererbung: erstreckt sich
Klassen in JavaScript unterstützen die Vererbung mit dem Schlüsselwort extens.
Im Ausdruck erweitert die Klasse Child Parent {}, die Child-Klasse erbt Konstruktoren, Felder und Methoden von Parent.
Erstellen wir eine untergeordnete Klasse ContentWriter, die die übergeordnete Klasse User erweitert:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
}
const writer = new ContentWriter('')
writer.name //
writer.getName() //
writer.posts // []
ContentWriter erbt vom Benutzer einen Konstruktor, eine getName () -Methode und ein Namensfeld. Der ContentWriter selbst definiert ein neues Beitragsfeld.
Beachten Sie, dass die privaten Felder und Methoden der übergeordneten Klasse nicht von den untergeordneten Klassen geerbt werden.
5.1. Übergeordneter Konstruktor: super () in Konstruktor ()
Verwenden Sie die spezielle Funktion super (), die im Konstruktor der untergeordneten Klasse verfügbar ist, um den Konstruktor der übergeordneten Klasse in der untergeordneten Klasse aufzurufen.
Lassen Sie den ContentWriter-Konstruktor den übergeordneten Konstruktor aufrufen und das Feld posts initialisieren:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
constructor(name, posts) {
super(name)
this.posts = posts
}
}
const writer = new ContentWriter('', [' '])
writer.name //
writer.posts // [' ']
super (name) in der untergeordneten Klasse ContentWriter ruft den übergeordneten Klassenkonstruktor User auf.
Beachten Sie, dass super () im untergeordneten Konstruktor aufgerufen wird, bevor das Schlüsselwort this verwendet wird. Der Aufruf von super () "bindet" den übergeordneten Konstruktor an die Instanz.
class Child extends Parent {
constructor(value1, value2) {
// !
this.prop2 = value2
super(value1)
}
}
5.2. Übergeordnete Instanz: Super in Methoden
Verwenden Sie die spezielle Super-Kurzform, um auf eine übergeordnete Methode innerhalb einer untergeordneten Klasse zuzugreifen:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
constructor(name, posts) {
super(name)
this.posts = posts
}
getName() {
const name = super.getName()
if (name === '') {
return ''
}
return name
}
}
const writer = new ContentWriter('', [' '])
writer.getName() //
getName () der untergeordneten ContentWriter-Klasse ruft die getName () -Methode der übergeordneten Klasse User auf.
Dies wird als Methodenüberschreibung bezeichnet.
Beachten Sie, dass super auch für statische Methoden der übergeordneten Klasse verwendet werden kann.
6. Objekttypprüfung: instanceof
Die Objektinstanz des Klassenausdrucks bestimmt, ob ein Objekt eine Instanz der angegebenen Klasse ist.
Betrachten wir ein Beispiel:
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
const user = new User('')
const obj = {}
user instanceof User // true
obj instanceof User // false
Die Instanz des Operators ist polymorph: Er untersucht die gesamte Klassenkette.
class User {
name
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
class ContentWriter extends User {
posts = []
constructor(name, posts) {
super(name)
this.posts = posts
}
}
const writer = new ContentWriter('', [' '])
writer instanceof ContentWriter // true
writer instanceof User // true
Was ist, wenn wir eine bestimmte Instanzklasse definieren müssen? Die Konstruktoreigenschaft kann hierfür verwendet werden:
writer.constructor === ContentWriter // true
writer.constructor === User // false
//
writer.__proto__ === ContentWriter.prototype // true
writer.__proto__ === User.prototype // false
7. Klassen und Prototypen
Es muss gesagt werden, dass die Klassensyntax eine schöne Abstraktion über die prototypische Vererbung ist. Sie müssen keine Prototypen referenzieren, um Klassen zu verwenden.
Klassen sind jedoch nur ein Überbau der prototypischen Vererbung. Jede Klasse ist eine Funktion, die beim Aufruf eines Konstruktors eine Instanz erstellt.
Die nächsten beiden Beispiele sind identisch.
Klassen:
class User {
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
const user = new User('')
user.getName() //
user instanceof User // true
Prototypen:
function User(name) {
this.name = name
}
User.prototype.getName = function () {
return this.name
}
const user = new User('')
user.getName() //
user instanceof User // true
Das Verständnis von Klassen erfordert daher gute Kenntnisse der prototypischen Vererbung.
8. Verfügbarkeit von Klassenfunktionen
Die in diesem Artikel vorgestellten Klassenfunktionen werden in der dritten Betrachtungsphase zwischen der ES6-Spezifikation und den Vorschlägen aufgeteilt:
- Öffentliche und private Instanzfelder
- Methoden und Accessoren für private Instanzen
- Öffentliche und private statische Felder und private statische Methoden
- ES6-Spezifikation
Ca. Per: Laut Kann ich verwenden, beträgt die Unterstützung für private Klassenfelder derzeit 68%.
9. Fazit
Klassen in JavaScript werden verwendet, um Instanzen mithilfe eines Konstruktors zu initialisieren und ihre Felder und Methoden zu definieren. Das statische Schlüsselwort kann verwendet werden, um die Felder und Methoden der Klasse selbst zu definieren.
Die Vererbung wird mithilfe des Schlüsselworts extensives implementiert. Mit dem Schlüsselwort super können Sie vom untergeordneten Element aus auf die übergeordnete Klasse zugreifen.
Um die Einkapselung auszunutzen, d.h. Interne Implementierungsdetails ausblenden, Felder und Methoden privat machen. Die Namen solcher Felder und Methoden müssen mit einem # -Symbol beginnen.
Klassen sind in modernem JavaScript allgegenwärtig.
Ich hoffe der Artikel war hilfreich für Sie. Danke für die Aufmerksamkeit.