Es gibt zwei Arten von Objekteigenschaften.

Die erste Art sind Dateneigenschaften. Wir wissen bereits, wie man mit ihnen arbeitet. Alle Eigenschaften, die wir bis jetzt verwendet haben, waren Dateneigenschaften.

Die zweite Art von Eigenschaften ist etwas Neues. Es sind Accessor-Eigenschaften. Sie sind im Wesentlichen Funktionen, die beim Erhalten und Setzen eines Wertes ausgeführt werden, aber für einen externen Code wie normale Eigenschaften aussehen.

Getter und Setter

Zugriffseigenschaften werden durch „Getter“- und „Setter“-Methoden dargestellt. In einem Objektliteral werden sie mit get und set bezeichnet:

let obj = { get propName() { // getter, the code executed on getting obj.propName }, set propName(value) { // setter, the code executed on setting obj.propName = value }};

Der Getter funktioniert, wenn obj.propName gelesen wird, der Setter – wenn er zugewiesen wird.

Wir haben zum Beispiel ein user-Objekt mit name und surname:

let user = { name: "John", surname: "Smith"};

Nun wollen wir eine fullName-Eigenschaft hinzufügen, die "John Smith" sein soll. Natürlich wollen wir keine vorhandenen Informationen kopieren, also können wir sie als Accessor implementieren:

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }};alert(user.fullName); // John Smith

Von außen sieht eine Accessor-Eigenschaft wie eine normale aus. Das ist die Idee von Accessor-Eigenschaften. Wir rufen user.fullName nicht als Funktion auf, sondern lesen sie ganz normal: der Getter läuft im Hintergrund.

Ab sofort hat fullName nur einen Getter. Wenn wir versuchen, user.fullName= zuzuweisen, wird es einen Fehler geben:

let user = { get fullName() { return `...`; }};user.fullName = "Test"; // Error (property has only a getter)

Lassen Sie uns das beheben, indem wir einen Setter für user.fullName hinzufügen:

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { = value.split(" "); }};// set fullName is executed with the given value.user.fullName = "Alice Cooper";alert(user.name); // Alicealert(user.surname); // Cooper

Als Ergebnis haben wir eine „virtuelle“ Eigenschaft fullName. Sie ist lesbar und beschreibbar.

Accessor-Deskriptoren

Deskriptoren für Accessor-Eigenschaften unterscheiden sich von denen für Dateneigenschaften.

Für Accessor-Eigenschaften gibt es keine value oder writable, sondern get und set Funktionen.

Das heißt, ein Accessor-Deskriptor kann haben:

  • get – eine Funktion ohne Argumente, die arbeitet, wenn eine Eigenschaft gelesen wird,
  • set – eine Funktion mit einem Argument, die aufgerufen wird, wenn die Eigenschaft gesetzt wird,
  • enumerable – wie bei den Dateneigenschaften,
  • configurable – wie bei den Dateneigenschaften.

Um zum Beispiel einen Accessor fullName mit defineProperty zu erstellen, können wir einen Deskriptor mit get und set übergeben:

let user = { name: "John", surname: "Smith"};Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { = value.split(" "); }});alert(user.fullName); // John Smithfor(let key in user) alert(key); // name, surname

Bitte beachten Sie, dass eine Eigenschaft entweder ein Accessor (hat get/set Methoden) oder eine Dateneigenschaft (hat ein value) sein kann, nicht beides.

Wenn wir versuchen, sowohl get als auch value in demselben Deskriptor anzugeben, wird ein Fehler auftreten:

// Error: Invalid property descriptor.Object.defineProperty({}, 'prop', { get() { return 1 }, value: 2});

Smartere Getter/Setter

Getter/Setter können als Wrapper über „echten“ Eigenschaftswerten verwendet werden, um mehr Kontrolle über Operationen mit ihnen zu erhalten.

Wenn wir zum Beispiel zu kurze Namen für user verbieten wollen, können wir einen Setter name haben und den Wert in einer separaten Eigenschaft _name aufbewahren:

let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters"); return; } this._name = value; }};user.name = "Pete";alert(user.name); // Peteuser.name = ""; // Name is too short...

So wird der Name in der Eigenschaft _name gespeichert, und der Zugriff erfolgt über Getter und Setter.

Technisch gesehen kann externer Code direkt auf den Namen zugreifen, indem er user._name verwendet. Aber es gibt eine weit verbreitete Konvention, dass Eigenschaften, die mit einem Unterstrich "_" beginnen, intern sind und nicht von außerhalb des Objekts berührt werden sollten.

Aus Kompatibilitätsgründen verwenden

Einer der großen Vorteile von Accessoren ist, dass sie es erlauben, jederzeit die Kontrolle über eine „normale“ Dateneigenschaft zu übernehmen, indem sie durch einen Getter und einen Setter ersetzt werden und ihr Verhalten verändern.

Stellen Sie sich vor, wir beginnen mit der Implementierung von Benutzerobjekten unter Verwendung der Dateneigenschaften name und age:

function User(name, age) { this.name = name; this.age = age;}let john = new User("John", 25);alert( john.age ); // 25

…Aber früher oder später können sich die Dinge ändern. Statt age können wir uns entscheiden, birthday zu speichern, weil es präziser und bequemer ist:

function User(name, birthday) { this.name = name; this.birthday = birthday;}let john = new User("John", new Date(1992, 6, 1));

Was machen wir nun mit dem alten Code, der immer noch die age-Eigenschaft verwendet?

Wir können versuchen, alle solchen Stellen zu finden und sie zu korrigieren, aber das braucht Zeit und kann schwierig sein, wenn dieser Code von vielen anderen Leuten benutzt wird. Und außerdem ist age eine nette Sache, die man in user haben kann, richtig?

Lasst es uns behalten.

Das Hinzufügen eines Getters für age löst das Problem:

function User(name, birthday) { this.name = name; this.birthday = birthday; // age is calculated from the current date and birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } });}let john = new User("John", new Date(1992, 6, 1));alert( john.birthday ); // birthday is availablealert( john.age ); // ...as well as the age

Jetzt funktioniert auch der alte Code und wir haben eine schöne zusätzliche Eigenschaft.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.