December 23, 2013
V JavaScriptu většinou platí, že co vidíte, to dostanete. Hodnota je hodnota, neexistují žádné triky. Někdy však chcete hodnotu, která je založena na nějakých jiných hodnotách: například něčí celé jméno je zřetězením jeho jména a příjmení. Pokud máte objekt person
a chcete, aby uživatelé tohoto objektu mohli nastavit celé, křestní nebo příjmení a aby se tato změna okamžitě projevila v ostatních hodnotách, konvenčně byste to sestavili pomocí funkcí:
person.setLastName('Smith');person.setFirstName('Jimmy');person.getFullName(); // Jimmy Smith
To je ale ošklivé a vyžaduje to, aby se uživatelé vašeho objektu starali o to, že vlastnosti spolu souvisejí; ve složitějším příkladu to nemusí být tak zřejmé jako u jmen. Naštěstí existuje lepší způsob, přidaný v ECMAScriptu 5.
Seznamte se s gettery a settery.
Jak
Vytvořme tento objekt osoby. Chceme, aby bylo možné nastavit jméno, příjmení nebo celé jméno a aby se zbylá dvě automaticky aktualizovala.
var person = { firstName: 'Jimmy', lastName: 'Smith', get fullName() { return this.firstName + ' ' + this.lastName; }, set fullName (name) { var words = name.toString().split(' '); this.firstName = words || ''; this.lastName = words || ''; }}person.fullName = 'Jack Franklin';console.log(person.firstName); // Jackconsole.log(person.lastName) // Franklin
Takže o co tu jde?
Klíčová slova get a set jsou důležitá. Následuje za nimi vlastnost, které se týkají (fullName
), a tělo funkce, které definuje chování při přístupu k vlastnosti (name = person.fullName
) nebo její změně (person.fullName = 'Some Name'
).
Tato dvě klíčová slova definují přístupové funkce: getter a setter pro vlastnost fullName
. Při přístupu k vlastnosti se použije návratová hodnota z getteru. Při nastavování hodnoty se zavolá setter a předá se mu nastavená hodnota. Je na vás, co s touto hodnotou uděláte, ale to, co se vrátí z setteru, je hodnota, která byla předána – nemusíte tedy nic vracet.
Oficiální způsob: Object.defineProperty
Kromě inline způsobu deklarace getterů a setterů to lze provést také explicitněji pomocí Object.defineProperty
(MDN dokumentace). Tato metoda přijímá tři argumenty. Prvním je objekt, ke kterému má být vlastnost přidána, druhým je název vlastnosti a třetím je objekt, který vlastnost popisuje (tzv. deskriptor vlastnosti). Zde je příklad, který kopíruje výše uvedený příklad:
var person = { firstName: 'Jimmy', lastName: 'Smith'};Object.defineProperty(person, 'fullName', {get: function() {return firstName + ' ' + lastName;},set: function(name) {var words = name.split(' ');this.firstName = words || '';this.lastName = words || '';}});
Výhoda zde není hned patrná. Kromě možnosti přidávat vlastnosti po vytvoření počátečního objektu, je v tom nějaká skutečná výhoda?“
Když definujete vlastnost tímto způsobem, můžete udělat mnohem více než jen definovat setter nebo getter. Můžete také předat následující klíče:
-
configurable
(false
ve výchozím nastavení): pokud je to pravda, konfiguraci vlastnosti bude možné v budoucnu měnit. -
enumerable
(false
ve výchozím nastavení): pokud je to pravda, vlastnost se zobrazí při procházení objektem (for (var key in obj)
).
Můžeme také definovat vlastnosti, které nemají explicitní gettery nebo settery:
Object.defineProperty(person, 'age', { value: 42});
Tímto způsobem se vytvoří person.age
a nastaví se na hodnotu 42. V případě, že se vytvoří person.age
, nastaví se na hodnotu 42. Je důležité si uvědomit, že tato vlastnost není zapisovatelná. Volání person.age = 99
nebude mít žádný účinek. Tímto způsobem můžete vytvářet vlastnosti pouze pro čtení. Pokud má vlastnost nastavený klíč value
, nemůže mít getter ani setter. Vlastnosti mohou mít hodnoty nebo accessory, ne obojí.
Nejen to, ale protože vlastnost enumerable
má výchozí hodnotu false
, tato vlastnost se nezobrazí, když procházíme smyčkou klíče objektu.
Pokud bychom chtěli, aby byla vlastnost zapisovatelná, museli bychom nastavit vlastnost writable
:
Object.defineProperty(person, 'age', { value: 42, writable: true});
Nyní bude mít person.age = 99;
požadovaný účinek.
Používání nadměrné
Pamatujte: jen proto, že nějaká vlastnost existuje, nemusí se používat stále. Gettery a settery mají své využití, ale nepřehánějte to, jinak s největší pravděpodobností skončíte s návrhem, který bude pro ty, kdo s vašimi objekty komunikují, matoucí. Při opatrném použití jsou velmi účinné. S velkou mocí však přichází i velká zodpovědnost.
Podpora prohlížečů?
IE9 a vyšší mají plnou podporu Object.defineProperty
, stejně jako Safari 5+, Firefox 4+, Chrome 5+ a Opera 12+. Pokud pracujete s Node.js, je zde plná podpora. Nemilujete prostě Node?“
Tento článek byl napsán společně s Tomem Ashworthem. Tomovi děkuji za pomoc při jeho sestavování.
Pokud se vám tento příspěvek líbil, přihlaste se k odběru novinek, abyste dostávali informace o novém obsahu 😎.
Všechny příspěvky na blogu
Pokud se vám tento příspěvek líbil, určitě mě sledujte na Twitteru.