Gettery a settery jsou funkce nebo metody používané k získávání a nastavování hodnot proměnných. Koncept getterů a setterů je v počítačovém programování běžný: téměř všechny vysokoúrovňové programovací jazyky přicházejí se sadou syntaxe pro implementaci getterů a setterů, včetně jazyka JavaScipt.
Přečtěte si také:
- Gettery-settery a zapouzdření
- Vytvoření getterů a setterů
- 1. Pomocí metod
- 2. Pomocí klíčových slov
- Který způsob je lepší?
- Předcházení přepisu
- Operace uvnitř getterů a setterů
- Ochrana dat pomocí getterů a setterů
- Nechráněná data
- 1. Rozsah bloku
- 2. Obor bloku může být vytvořen i mimo blok. Rozsah funkce
- 3. Ochrana dat bez scopingu
- Kdy byste měli používat gettery a settery?
Gettery-settery a zapouzdření
O myšlence getterů a setterů se vždy mluví ve spojení se zapouzdřením. Zapouzdření lze chápat dvěma způsoby.
Prvním je nastavení trojice getters-setters pro přístup k datům a jejich modifikaci. Tato definice je užitečná, když je třeba s daty před jejich uložením nebo zobrazením provést nějaké operace, jako je například validace – gettery a settery pro to poskytují ideální domov.
Druhá, existuje přísnější definice, podle které se zapouzdření provádí za účelem skrytí dat, aby byla nepřístupná z jiného kódu, než prostřednictvím getterů a setterů. Tímto způsobem se nám nestane, že důležitá data omylem přepíšeme nějakým jiným kódem v programu.
Vytvoření getterů a setterů
1. Pomocí metod
Protože gettery a settery jsou v podstatě funkce, které získávají/změňují hodnotu, existuje více způsobů, jak je vytvořit a použít. První způsob je:
var obj = { foo: 'this is the value of foo', getFoo: function() { return this.foo; }, setFoo: function(val) { this.foo = val; }}console.log(obj.getFoo());// "this is the value of foo"obj.setFoo('hello');console.log(obj.getFoo());// "hello"
Jedná se o nejjednodušší způsob vytváření getterů a setterů. Existuje vlastnost foo
a k ní dvě metody: getFoo
a setFoo
pro vrácení a přiřazení hodnoty této vlastnosti.
2. Pomocí klíčových slov
„Oficiálnější“ a robustnější způsob vytváření getterů a setterů je pomocí klíčových slov get
a set
.
Chcete-li vytvořit getter, umístěte klíčové slovo get
před deklaraci funkce, která bude sloužit jako metoda getteru, a stejným způsobem použijte klíčové slovo set
pro vytvoření setteru. Syntaxe je následující:
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}console.log(obj.foo);// "this is the value of foo"obj.foo = 'hello';console.log(obj.foo);// "hello"
Všimněte si, že data mohou být uložena pouze pod názvem vlastnosti (fooVal
), který se liší od názvu metody getter-setter (foo
), protože vlastnost, která obsahuje getter-setter, nemůže obsahovat také data.
Který způsob je lepší?
Pokud se rozhodnete vytvářet gettery a settery pomocí klíčových slov, můžete použít operátor přiřazení pro nastavení dat a operátor tečky pro získání dat stejným způsobem, jakým byste přistupovali/nastavovali hodnotu běžné vlastnosti.
Jestliže se však rozhodnete pro první způsob kódování getterů a setterů, budete muset metody setterů a getterů volat pomocí syntaxe volání funkcí, protože se jedná o typické funkce (nic speciálního jako ty vytvořené pomocí klíčových slov get
a set
).
Také existuje možnost, že nakonec omylem přiřadíte vlastnostem, které držely tyto metody getterů a setterů, nějakou jinou hodnotu a zcela o ně přijdete! Něčeho takového se u pozdějšího způsobu nemusíte obávat.
Je vám tedy jasné, proč jsem řekl, že druhá technika je robustnější.
Předcházení přepisu
Pokud z nějakého důvodu dáváte přednost první technice, vytvořte vlastnosti, které drží metody getter-setter, pouze pro čtení pomocí Object.defineProperties
. Vlastnosti vytvořené pomocí Object.defineProperties
, Object.defineProperty
a Reflect.defineProperty
se automaticky konfigurují na writable: false
, což znamená pouze pro čtení:
/* Overwrite prevention */var obj = { foo: 'this is the value of foo'};Object.defineProperties(obj, { 'getFoo': { value: function () { return this.foo; } }, 'setFoo': { value: function (val) { this.foo = val; } }});obj.getFoo = 66;// getFoo is not going to be overwritten!console.log(obj.getFoo());// "this is the value of foo"
Operace uvnitř getterů a setterů
Jakmile zavedete gettery a settery, můžete se pustit do provádění operací s daty před jejich změnou nebo vrácením.
V níže uvedeném kódu jsou ve funkci getter data před vrácením spojena s řetězcem a ve funkci setter je před aktualizací provedena validace, zda je hodnota číslo, či nikoliv n
.
var obj = { n: 67, get id() { return 'The ID is: ' + this.n; }, set id(val) { if (typeof val === 'number') this.n = val; }}console.log(obj.id);// "The ID is: 67"obj.id = 893;console.log(obj.id);// "The ID is: 893"obj.id = 'hello';console.log(obj.id);// "The ID is: 893"
Ochrana dat pomocí getterů a setterů
Dosud jsme se zabývali použitím getterů a setterů v prvním kontextu zapouzdření. Přejděme k druhému, tj. jak pomocí getterů a setterů skrýt data před vnějším kódem.
Nechráněná data
Nastavení getterů a setterů neznamená, že k datům lze přistupovat a měnit je pouze prostřednictvím těchto metod. V následujícím příkladu je měníme přímo, aniž bychom se dotkli metod getter a setter:
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}obj.fooVal = 'hello';console.log(obj.foo);// "hello"
Nepoužili jsme setter, ale přímo jsme změnili data (fooVal
). Data, která jsme původně nastavili uvnitř obj
, jsou nyní pryč! Aby k tomu (náhodou) nedošlo, je třeba data nějak chránit. Tu můžete přidat tak, že omezíte rozsah toho, kde jsou vaše data dostupná. Toho můžete dosáhnout buď blokovým rozsahem, nebo rozsahem funkce.
1. Rozsah bloku
Jedním ze způsobů je použití rozsahu bloku, uvnitř kterého budou data definována pomocí klíčového slova let
, které omezí jeho rozsah na tento blok.
Obsah bloku lze vytvořit umístěním vašeho kódu do dvojice kudrnatých závorek. Kdykoli vytvoříte obor bloku, nezapomeňte nad ním zanechat komentář s žádostí o ponechání závorek, aby nikdo omylem závorky neodstranil v domnění, že jde o nějaké nadbytečné závorky v kódu navíc, nebo aby k oboru bloku nepřidal popisek.
/* BLOCK SCOPE, leave the braces alone! */{let fooVal = 'this is the value of foo';var obj = { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect the fooVal inside the blockconsole.log(obj.foo);// "this is the value of foo"
Změna/vytvoření fooVal
vně bloku neovlivní fooVal
uvedené uvnitř getterů setterů.
2. Obor bloku může být vytvořen i mimo blok. Rozsah funkce
Obvyklejší způsob ochrany dat pomocí rozsahu je udržování dat uvnitř funkce a vracení objektu s gettery a settery z této funkce.
function myobj(){ var fooVal = 'this is the value of foo'; return { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect our original fooValvar obj = myobj();console.log(obj.foo);// "this is the value of foo"
Objekt (s getterem a setterem foo()
uvnitř) vrácený funkcí myobj()
se uloží do obj
a pak se obj
použije k volání getteru a setteru.
3. Ochrana dat bez scopingu
Existuje také další způsob, jak lze data chránit před přepsáním bez omezení jejich rozsahu. Logika, která za tím stojí, je následující: Jak můžete změnit kus dat, když nevíte, jak se jmenují?
Pokud mají data ne tak snadno reprodukovatelný název proměnné/vlastnosti, je pravděpodobné, že je nikdo (ani my sami) nakonec nepřepisuje tím, že této proměnné/vlastnosti přiřadí nějakou hodnotu.
var obj = { s89274934764: 'this is the value of foo', get foo() { return this.s89274934764; }, set foo(val) { this.s89274934764 = val; }}console.log(obj.foo);// "this is the value of foo"
Vidíte, to je jeden ze způsobů, jak věci vyřešit. I když název, který jsem zvolil, není úplně dobrý, můžete také použít náhodné hodnoty nebo symboly pro vytvoření názvů vlastností, jak to navrhuje Derick Bailey v tomto příspěvku na blogu. Hlavním cílem je udržet data skrytá před ostatním kódem a nechat k nim přistupovat/aktualizovat dvojici getter-setter.
Kdy byste měli používat gettery a settery?
Teď přichází velká otázka: Začnete teď přiřazovat gettery a settery všem svým datům?
Pokud data skrýváš, pak ti nic jiného nezbývá.
Ale pokud jsou tvá data viditelná jiným kódem v pořádku, musíš stále používat gettery settery jen proto, abys je spojil s kódem, který s nimi provádí nějaké operace? Řekl bych, že ano. Kód se velmi brzy sčítá. Vytvoření mikrojednotek jednotlivých dat s vlastním getterem-setterem vám poskytuje určitou nezávislost, abyste mohli pracovat se zmíněnými daty bez ovlivnění ostatních částí kódu.
Přečtěte si také: 10 důvodů, proč potřebujete optimalizaci kódu
.