Existují dva druhy vlastností objektu.

Prvním druhem jsou datové vlastnosti. S nimi již umíme pracovat. Všechny vlastnosti, které jsme dosud používali, byly datové vlastnosti.

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

Gettery a settery

Vlastnosti accessor 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 při čtení obj.propName, setter při přiřazování.

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

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

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

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

Zvenčí vypadá vlastnost accessor jako běžná. To je myšlenka vlastností accessor. Vlastnost user.fullName nevoláme jako funkci, ale normálně ji čteme: getter běží za scénou.

Od této chvíle má fullName pouze getter. 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)

Napravíme to přidáním setteru pro user.fullName:

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

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

Deskriptory přístupových vlastností

Deskriptory pro přístupové vlastnosti se liší od deskriptorů pro datové vlastnosti.

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

To znamená, že deskriptor accessoru může mít:

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

Příklad pro vytvoření accessoru 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) { = value.split(" "); }});alert(user.fullName); // John Smithfor(let key in user) alert(key); // name, surname

Upozorňujeme, že vlastnost může být buď accessor (má metody get/set), nebo datová vlastnost (má value), nikoli obojí.

Pokusíme-li se v jednom deskriptoru zadat jak get, tak value, dojde k chybě:

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

Silnější gettery/settery

Gettery/settery lze použít jako obal nad „skutečnými“ hodnotami vlastností a získat tak větší kontrolu nad operacemi s nimi.

Pokud například chceme zakázat příliš krátká jména pro user, můžeme mít setter name a hodnotu uchovávat 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); // Peteuser.name = ""; // Name is too short...

Takže jméno je uloženo ve vlastnosti _name a přístup se provádí přes getter a setter.

Technicky je externí kód schopen přistupovat k názvu přímo pomocí user._name. Existuje však všeobecně známá konvence, že vlastnosti začínající podtržítkem "_" jsou interní a nemělo by se na ně sahat z vnějšku objektu.

Použití pro kompatibilitu

Jedním z velkých využití accessorů je, že umožňují kdykoli převzít kontrolu nad „běžnou“ datovou vlastností tím, že ji nahradí getterem a setterem a upraví její chování.

Představte si, že bychom 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

… Dříve nebo později se však věci mohou změnit. Místo age se možná rozhodneme ukládat birthday, protože je to 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));

Teď co dělat se starým kódem, který stále používá vlastnost age?

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

Ponechme si ji.

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

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

Teď funguje i starý kód a my máme pěknou vlastnost navíc.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.