Es gibt zwei Arten von Objekteigenschaften.
Die erste Art sind Dateneigenschaften . Wir wissen bereits, wie man mit ihnen arbeitet. Alle Eigenschaften, die wir bisher verwendet haben, waren Dateneigenschaften.
Der zweite Immobilientyp ist etwas Neues. Es handelt sich um eine Accessor-Eigenschaft . Dabei handelt es sich im Wesentlichen um Funktionen, die beim Abrufen und Festlegen eines Werts ausgeführt werden, für einen externen Code jedoch wie normale Eigenschaften aussehen.
Accessor-Eigenschaften werden durch die Methoden „Getter“ und „Setter“ dargestellt. In einem Objektliteral werden sie durch get
und set
bezeichnet:
sei obj = { get propName() { // Getter, der Code, der beim Abrufen von obj.propName ausgeführt wird }, set propName(value) { // Setter, der Code, der beim Setzen von obj.propName = value ausgeführt wird } };
Der Getter funktioniert, wenn obj.propName
gelesen wird, der Setter – wenn er zugewiesen wird.
Zum Beispiel haben wir ein user
mit name
und surname
:
let user = { Name: „John“, Nachname: „Smith“ };
Jetzt möchten wir eine fullName
Eigenschaft hinzufügen, die "John Smith"
lauten sollte. Natürlich möchten wir vorhandene Informationen nicht kopieren und einfügen, daher können wir sie als Accessor implementieren:
let user = { Name: „John“, Nachname: „Smith“, get fullName() { return `${this.name} ${this.surname}`; } }; alarm(user.fullName); // John Smith
Von außen sieht eine Accessor-Eigenschaft wie eine normale Eigenschaft aus. Das ist die Idee von Accessor-Eigenschaften. Wir rufen user.fullName
nicht als Funktion auf, sondern lesen es normal: Der Getter läuft im Hintergrund.
Derzeit verfügt fullName
nur über einen Getter. Wenn wir versuchen, user.fullName=
zuzuweisen, wird ein Fehler angezeigt:
let user = { get fullName() { return `...`; } }; user.fullName = "Test"; // Fehler (Eigenschaft hat nur einen Getter)
Beheben wir das Problem, indem wir einen Setter für user.fullName
hinzufügen:
let user = { Name: „John“, Nachname: „Smith“, get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { [dieser.Name, dieser.Nachname] = value.split(" "); } }; // set fullName wird mit dem angegebenen Wert ausgeführt. user.fullName = „Alice Cooper“; Alert(Benutzername); // Alice Alert(Benutzer.Nachname); // Cooper
Als Ergebnis haben wir eine „virtuelle“ Eigenschaft fullName
. Es ist lesbar und beschreibbar.
Deskriptoren für Accessor-Eigenschaften unterscheiden sich von denen für Dateneigenschaften.
Für Accessor-Eigenschaften gibt es weder value
noch writable
, sondern stattdessen get
und set
-Funktionen.
Das heißt, ein Accessor-Deskriptor kann Folgendes haben:
get
– eine Funktion ohne Argumente, die funktioniert, wenn eine Eigenschaft gelesen wird,
set
– eine Funktion mit einem Argument, die aufgerufen wird, wenn die Eigenschaft gesetzt wird,
enumerable
– dasselbe wie für Dateneigenschaften,
configurable
– wie für Dateneigenschaften.
Um beispielsweise einen Accessor fullName
mit defineProperty
zu erstellen, können wir einen Deskriptor mit get
und set
übergeben:
let user = { Name: „John“, Nachname: „Smith“ }; Object.defineProperty(user, 'fullName', { erhalten() { return `${this.name} ${this.surname}`; }, set(Wert) { [dieser.Name, dieser.Nachname] = value.split(" "); } }); alarm(user.fullName); // John Smith for(Benutzer eingeben lassen) alarm(key); // Name, Nachname
Bitte beachten Sie, dass eine Eigenschaft entweder ein Accessor (hat get/set
-Methoden) oder eine Dateneigenschaft (hat einen value
) sein kann, nicht beides.
Wenn wir versuchen, sowohl get
als auch value
im selben Deskriptor anzugeben, tritt ein Fehler auf:
// Fehler: Ungültiger Eigenschaftsdeskriptor. Object.defineProperty({}, 'prop', { erhalten() { Rückkehr 1 }, Wert: 2 });
Getter/Setter können als Wrapper für „echte“ Eigenschaftswerte verwendet werden, um mehr Kontrolle über die damit verbundenen Vorgänge zu erlangen.
Wenn wir beispielsweise zu kurze Namen für user
verbieten möchten, können wir einen Setter name
verwenden und den Wert in einer separaten Eigenschaft _name
behalten:
let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alarm("Name ist zu kurz, mindestens 4 Zeichen erforderlich"); zurückkehren; } this._name = value; } }; user.name = "Pete"; Alert(Benutzername); // Pete user.name = ""; // Name ist zu kurz...
Der Name wird also in der Eigenschaft _name
gespeichert und der Zugriff erfolgt über Getter und Setter.
Technisch gesehen kann externer Code mithilfe von user._name
direkt auf den Namen zugreifen. Es gibt jedoch eine weithin bekannte Konvention, dass Eigenschaften, die mit einem Unterstrich "_"
beginnen, intern sind und nicht von außerhalb des Objekts berührt werden sollten.
Einer der großartigen Einsatzmöglichkeiten von Accessoren besteht darin, dass sie es ermöglichen, jederzeit die Kontrolle über eine „normale“ Dateneigenschaft zu übernehmen, indem man sie durch einen Getter und einen Setter ersetzt und ihr Verhalten optimiert.
Stellen Sie sich vor, wir beginnen mit der Implementierung von Benutzerobjekten mithilfe der Dateneigenschaften name
und age
:
Funktion Benutzer(Name, Alter) { this.name = Name; this.age = Alter; } let john = new User("John", 25); alarm( john.age ); // 25
…Aber früher oder später können sich die Dinge ändern. Anstelle des age
können wir uns auch dafür entscheiden, birthday
zu speichern, da dies präziser und praktischer ist:
Funktion Benutzer(Name, Geburtstag) { this.name = Name; this.birthday = Geburtstag; } let john = new User("John", new Date(1992, 6, 1));
Was tun nun mit dem alten Code, der immer noch age
verwendet?
Wir können versuchen, alle derartigen Stellen zu finden und zu reparieren, aber das braucht Zeit und kann schwierig sein, wenn dieser Code von vielen anderen Leuten verwendet wird. Und außerdem ist age
eine nette Sache bei user
, oder?
Behalten wir es.
Das Hinzufügen eines Getters für age
löst das Problem:
Funktion Benutzer(Name, Geburtstag) { this.name = Name; this.birthday = Geburtstag; // Alter wird aus dem aktuellen Datum und Geburtstag berechnet Object.defineProperty(this, "age", { erhalten() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } }); } let john = new User("John", new Date(1992, 6, 1)); alarm( john.birthday ); // Geburtstag ist verfügbar alarm( john.age ); // ...sowie das Alter
Jetzt funktioniert auch der alte Code und wir haben eine schöne zusätzliche Eigenschaft.