Zařízení pro získání a nastavení vlastností

Existují dva druhy vlastností objektu.

Prvním druhem jsou vlastnosti dat . Už víme, jak s nimi pracovat. Všechny vlastnosti, které jsme dosud používali, byly datové vlastnosti.

Druhý typ nemovitosti je něco nového. Je to vlastnost přístupového nástroje . Jsou to v podstatě funkce, které se provádějí při získávání a nastavení hodnoty, ale vypadají jako běžné vlastnosti externího kódu.

Getters and setters

Vlastnosti přístupového objektu jsou reprezentovány metodami „getter“ a „setter“. V objektovém literálu jsou označeny get a set :

let obj = {
 get propName() {
 // getter, the code executed on getting obj.propName
 },

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

Getter funguje, když obj.propName je přečten, nastavovač – když je přiřazen.

Například máme user objekt s name a surname :

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

Nyní chceme přidat fullName vlastnost, která by měla být "John Smith" . Samozřejmě nechceme kopírovat a vkládat existující informace, takže je můžeme implementovat jako přístupový objekt:

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

 get fullName() {
 return `${this.name} ${this.surname}`;
 }
};

alert(user.fullName); // John Smith

Zvenčí vypadá vlastnost přístupového objektu jako běžná. To je myšlenka vlastností přístupového prvku. Nevoláme user.fullName jako funkci čteme normálně:getter běží za scénou.

Od této chvíle fullName má pouze getr. Pokud se pokusíme přiřadit user.fullName= , dojde k chybě:

let user = {
 get fullName() {
 return `...`;
 }
};

user.fullName = "Test"; // Error (property has only a getter)

Pojďme to opravit přidáním setteru pro user.fullName :

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

 get fullName() {
 return `${this.name} ${this.surname}`;
 },

 set fullName(value) {
 [this.name, this.surname] = value.split(" ");
 }
};

// set fullName is executed with the given value.
user.fullName = "Alice Cooper";

alert(user.name); // Alice
alert(user.surname); // Cooper

Výsledkem je „virtuální“ vlastnost fullName . Je čitelný a zapisovatelný.

Deskriptory přístupových práv

Deskriptory pro vlastnosti přístupového objektu se liší od deskriptorů pro vlastnosti dat.

Pro vlastnosti přístupového objektu neexistuje value nebo writable , ale místo toho existuje get a set funkce.

To znamená, že popisovač přístupového objektu může mít:

  • get – funkce bez argumentů, která funguje při čtení vlastnosti,
  • set – funkce s jedním argumentem, která se volá při nastavení vlastnosti,
  • enumerable – stejně jako u vlastností dat,
  • configurable – stejné jako u vlastností dat.

Chcete-li například vytvořit přístupový objekt fullName s defineProperty , můžeme předat deskriptor s get a set :

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

Object.defineProperty(user, 'fullName', {
 get() {
 return `${this.name} ${this.surname}`;
 },

 set(value) {
 [this.name, this.surname] = value.split(" ");
 }
});

alert(user.fullName); // John Smith

for(let key in user) alert(key); // name, surname

Upozorňujeme, že vlastnost může být buď přístupovým objektem (má get/set metody) nebo vlastnost data (má value ), ne obojí.

Pokud se pokusíme dodat obě get a value ve stejném deskriptoru bude chyba:

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

 value: 2
});

Chytřejší getters/setters

Getters/setters lze použít jako obaly nad „skutečnými“ hodnotami vlastností, abyste získali větší kontrolu nad operacemi s nimi.

Například pokud chceme zakázat příliš krátké názvy pro user , můžeme mít setter name a ponechte hodnotu v samostatné vlastnosti _name :

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); // Pete

user.name = ""; // Name is too short...

Jméno je tedy uloženo v _name vlastnost a přístup se provádí pomocí getter a setter.

Technicky je externí kód schopen přistupovat k názvu přímo pomocí user._name . Existuje však široce známá konvence, že vlastnosti začínající podtržítkem "_" jsou vnitřní a neměli byste se jich dotýkat zvenčí objektu.

Použití pro kompatibilitu

Jedním z velkých využití přístupových prvků je to, že umožňují kdykoli převzít kontrolu nad „běžnou“ datovou vlastností tím, že ji nahradí getter a setter a vyladí její chování.

Představte si, že jsme začali implementovat uživatelské objekty pomocí datových vlastností name a age :

function User(name, age) {
 this.name = name;
 this.age = age;
}

let john = new User("John", 25);

alert( john.age ); // 25

…Ale dříve nebo později se věci mohou změnit. Místo age můžeme se rozhodnout uložit birthday , protože je přesnější a pohodlnější:

function User(name, birthday) {
 this.name = name;
 this.birthday = birthday;
}

let john = new User("John", new Date(1992, 6, 1));

Co dělat se starým kódem, který stále používá age majetek?

Můžeme se pokusit najít všechna taková místa a opravit je, ale to vyžaduje čas a může to být těžké, pokud tento kód používá mnoho dalších lidí. A kromě toho age je hezké mít v user , že?

Nechme to.

Přidání getteru pro age řeší problém:

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 available
alert( john.age ); // ...as well as the age

Nyní funguje i starý kód a máme hezkou další vlastnost.