Vlastnosti objektů se dÄlà do dvou druhů.
Prvnà druh jsou datové vlastnosti. S nimi už pracovat umÃme. VÅ¡echny vlastnosti, které jsme až doposud použÃvali, byly datové vlastnosti.
Druhý druh vlastnostà je nÄco nového. Jsou to pÅÃstupové vlastnosti. Jsou to v zásadÄ funkce, které se spustà pÅi naÄÃtánà a nastavovánà hodnoty, ale v externÃm kódu vypadajà jako bÄžné vlastnosti.
Gettery a settery
PÅÃstupové vlastnosti jsou pÅedstavovány metodami nazývanými âgetterâ a âsetterâ. V objektovém literálu jsou oznaÄeny get a set:
let obj = {
get názevVlastnosti() {
// getter, kód spuÅ¡tÄný pÅi naÄÃtánà obj.názevVlastnosti
},
set názevVlastnosti(hodnota) {
// setter, kód spuÅ¡tÄný pÅi nastavovánà obj.názevVlastnosti = hodnota
}
};
Getter se spustÃ, když je vlastnost obj.názevVlastnosti naÄÃtána, setter se spustÃ, když je mÄnÄna.
NapÅÃklad máme objekt uživatel s vlastnostmi jméno a pÅÃjmenÃ:
let uživatel = {
jméno: "Jan",
pÅÃjmenÃ: "Novák"
};
Nynà chceme pÅidat vlastnost celéJméno, která by mÄla být "Jan Novák". SamozÅejmÄ nechceme kopÃrovat a vkládat existujÃcà informaci, a tak ji můžeme implementovat jako pÅÃstupovou:
let uživatel = {
jméno: "Jan",
pÅÃjmenÃ: "Novák",
get celéJméno() {
return `${this.jméno} ${this.pÅÃjmenÃ}`;
}
};
alert(uživatel.celéJméno); // Jan Novák
ZvenÄà se pÅÃstupová vlastnost jevà jako obyÄejná. To je myÅ¡lenka pÅÃstupových vlastnostÃ. Nevoláme uživatel.celéJméno jako funkci, ale normálnÄ ji pÅeÄteme: getter se spustà za scénou.
Dosud má celéJméno jen getter. Jestliže se pokusÃme pÅiÅadit uživatel.celéJméno=, nastane chyba:
let uživatel = {
get celéJméno() {
return `...`;
}
};
uživatel.celéJméno = "Test"; // Chyba (vlastnost má jen getter)
Opravme ji pÅidánÃm setteru pro uživatel.celéJméno:
let uživatel = {
jméno: "Jan",
pÅÃjmenÃ: "Novák",
get celéJméno() {
return `${this.jméno} ${this.pÅÃjmenÃ}`;
},
set celéJméno(hodnota) {
[this.jméno, this.pÅÃjmenÃ] = hodnota.split(" ");
}
};
// nastavenà vlastnosti celéJméno se spustà se zadanou hodnotou.
uživatel.celéJméno = "Alice Cooper";
alert(uživatel.jméno); // Alice
alert(uživatel.pÅÃjmenÃ); // Cooper
Výsledkem je, že máme âvirtuálnÃâ vlastnost celéJméno. Lze ji ÄÃst i do nà zapisovat.
Deskriptory pÅÃstupových vlastnostÃ
Deskriptory pÅÃstupových vlastnostà se lišà od deskriptorů datových vlastnostÃ.
U pÅÃstupových vlastnostà v nich nejsou value nebo writable, ale mÃsto nich tam jsou funkce get a set.
To znamená, že pÅÃstupový deskriptor může mÃt:
getâ funkce bez argumentů, která se spustà pÅi Ätenà vlastnosti,setâ funkce s jednÃm argumentem, která je volána, když se vlastnost nastavuje,enumerableâ totéž jako u datových vlastnostÃ,configurableâ totéž jako u datových vlastnostÃ.
NapÅÃklad chceme-li vytvoÅit pÅÃstupovou vlastnost celéJméno pomocà metody defineProperty, můžeme pÅedat deskriptor s get a set:
let uživatel = {
jméno: "Jan",
pÅÃjmenÃ: "Novák"
};
Object.defineProperty(uživatel, 'celéJméno', {
get() {
return `${this.jméno} ${this.pÅÃjmenÃ}`;
},
set(hodnota) {
[this.jméno, this.pÅÃjmenÃ] = hodnota.split(" ");
}
});
alert(uživatel.celéJméno); // Jan Novák
for(let klÃÄ in uživatel) alert(klÃÄ); // jméno, pÅÃjmenÃ
ProsÃme vÅ¡imnÄte si, že vlastnost může být buÄ pÅÃstupová (má metody get/set), nebo datová (má value), ale ne obojÃ.
Pokud se pokusÃme ve stejném deskriptoru poskytnout get i value, nastane chyba:
// Chyba: Neplatný deskriptor vlastnosti.
Object.defineProperty({}, 'vlastnost', {
get() {
return 1
},
value: 2
});
ChytÅejšà gettery a settery
Gettery a settery můžeme použÃvat jako obaly nad âskuteÄnýmiâ hodnotami vlastnostÃ, abychom nad operacemi s nimi zÃskali vÄtšà kontrolu.
NapÅÃklad jestliže chceme v objektu uživatel zakázat pÅÃliÅ¡ krátká jména, můžeme mÃt setter jméno a udržovat hodnotu v oddÄlené vlastnosti _jméno:
let uživatel = {
get jméno() {
return this._jméno;
},
set jméno(hodnota) {
if (hodnota.length < 4) {
alert("Jméno je pÅÃliÅ¡ krátké, musà mÃt alespoÅ 4 znaky");
return;
}
this._jméno = hodnota;
}
};
uživatel.jméno = "Petr";
alert(uživatel.jméno); // Petr
uživatel.jméno = ""; // Jméno je pÅÃliÅ¡ krátké...
Jméno je tedy uloženo ve vlastnosti _jméno a pÅÃstup k nÄmu se dÄje pÅes getter a setter.
Technicky může externà kód pÅistupovat pÅÃmo ke jménu pomocà uživatel._jméno. Podle Å¡iroce rozÅ¡ÃÅené konvence vÅ¡ak vlastnosti zaÄÃnajÃcà podtržÃtkem "_" jsou internà a nemÄlo by se na nÄ sahat zvnÄjÅ¡ku objektu.
PoužÃvánà pro kompatibilitu
JednÃm z vynikajÃcÃch využità pÅÃstupových vlastnostà je, že umožÅujà kdykoli pÅevzÃt kontrolu nad âobyÄejnouâ datovou vlastnostà tÃm, že ji nahradÃme getterem a setterem a vylepÅ¡Ãme jejà chovánÃ.
PÅedstavme si, že jsme zaÄali implementovat objekty uživatelů s použitÃm datových vlastnostà jméno a vÄk:
function Uživatel(jméno, vÄk) {
this.jméno = jméno;
this.vÄk = vÄk;
}
let jan = new Uživatel("Jan", 25);
alert( jan.vÄk ); // 25
â¦DÅÃve nebo pozdÄji se to vÅ¡ak může zmÄnit. MÃsto vlastnosti vÄk se můžeme rozhodnout ukládat datumNarozenÃ, protože je to pÅesnÄjšà a vhodnÄjÅ¡Ã:
function Uživatel(jméno, datumNarozenÃ) {
this.jméno = jméno;
this.datumNarozenà = datumNarozenÃ;
}
let jan = new Uživatel("Jan", new Date(1992, 6, 1));
Co teÄ udÄláme se starým kódem, který stále použÃvá vlastnost vÄk?
Můžeme se pokusit vÅ¡echna taková mÃsta najÃt a opravit, ale to nám zabere Äas a může to být tÄžko proveditelné, jestliže tento kód použÃvá mnoho dalÅ¡Ãch lidÃ. A kromÄ toho je hezké mÃt vÄk v objektu uživatel, ne?
Necháme si ho.
Problém vyÅešà pÅidánà getteru pro vÄk:
function Uživatel(jméno, datumNarozenÃ) {
this.jméno = jméno;
this.datumNarozenà = datumNarozenÃ;
// vÄk se vypoÄÃtá z dneÅ¡nÃho data a z data narozenÃ
Object.defineProperty(this, "vÄk", {
get() {
let dneÅ¡nÃRok = new Date().getFullYear();
return dneÅ¡nÃRok - this.datumNarozenÃ.getFullYear();
}
});
}
let jan = new Uživatel("Jan", new Date(1992, 6, 1));
alert( jan.datumNarozenà ); // datumNarozenà je k dispozici
alert( jan.vÄk ); // ...stejnÄ jako vÄk
Nynà bude fungovat i starý kód a navÃc jsme zÃskali dalšà pÄknou vlastnost.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)