Gettery i settery są funkcjami lub metodami używanymi do uzyskiwania i ustawiania wartości zmiennych. Koncepcja getter-setter jest powszechna w programowaniu komputerowym: prawie wszystkie języki programowania wysokiego poziomu są wyposażone w zestaw składni do implementacji getterów i setterów, w tym JavaScipt.
Read Also: 4 Useful JavaScript Statements You Should Know
W tym poście zobaczymy, czym są gettery settery oraz jak je tworzyć i używać w JavaScript.
- Getters-setters i enkapsulacja
- Tworzenie getterów i setterów
- 1. Za pomocą metod
- 2. Za pomocą słów kluczowych
- Który sposób jest lepszy?
- Zapobieganie nadpisywaniu
- Operacje wewnątrz getterów i setterów
- Chroń dane za pomocą getterów i setterów
- Niechronione dane
- 1. Skalowanie blokowe
- 2. Function scoping
- 3. Ochrona danych bez scopingu
- Kiedy powinieneś używać getterów i seterów?
Getters-setters i enkapsulacja
Pomysł getterów i setterów jest zawsze wymieniany w połączeniu z enkapsulacją. Enkapsulacja może być rozumiana na dwa sposoby.
Po pierwsze, jest to ustawianie trio getters-setters danych w celu uzyskania dostępu i modyfikacji tych danych. Ta definicja jest przydatna, gdy niektóre operacje, takie jak sprawdzanie poprawności, muszą być wykonane na danych przed ich zapisaniem lub wyświetleniem – gettery i settery stanowią dla nich idealny dom.
Po drugie, istnieje bardziej ścisła definicja, zgodnie z którą enkapsulacja jest wykonywana w celu ukrycia danych, aby uczynić je niedostępnymi dla innego kodu, z wyjątkiem getterów i setterów. Dzięki temu nie kończymy przypadkowo nadpisując ważne dane jakimś innym kodem w programie.
Tworzenie getterów i setterów
1. Za pomocą metod
Ponieważ gettery i settery są w zasadzie funkcjami pobierającymi/zmieniającymi wartość, istnieje więcej niż jeden sposób na ich utworzenie i użycie. Pierwszy sposób to:
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"
Jest to najprostszy sposób tworzenia getterów i setterów. Jest właściwość foo
i są dwie metody: getFoo
i setFoo
do zwracania i przypisywania wartości tej właściwości.
2. Za pomocą słów kluczowych
Bardziej „oficjalnym” i solidnym sposobem tworzenia getterów i setterów jest użycie słów kluczowych get
i set
.
Aby utworzyć getter, umieść słowo kluczowe get
przed deklaracją funkcji, która będzie służyć jako metoda gettera, i użyj słowa kluczowego set
w ten sam sposób, aby utworzyć setter. Składnia jest następująca:
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"
Zauważ, że dane mogą być przechowywane tylko pod nazwą właściwości (fooVal
), która jest inna niż nazwa metody gettera (foo
), ponieważ właściwość przechowująca metodę gettera nie może również przechowywać danych.
Który sposób jest lepszy?
Jeśli zdecydujesz się na tworzenie getterów i setterów za pomocą słów kluczowych, możesz użyć operatora przypisania do ustawienia danych i operatora kropki do uzyskania danych, w taki sam sposób, w jaki uzyskujesz dostęp do/ustawiasz wartość zwykłej właściwości.
Jednakże, jeśli wybierzesz pierwszy sposób kodowania getterów i setterów, musisz wywoływać metody setterów i getterów za pomocą składni wywołania funkcji, ponieważ są to typowe funkcje (nic specjalnego, jak te utworzone za pomocą słów kluczowych get
i set
).
Jest też szansa, że możesz przypadkowo przypisać jakąś inną wartość do właściwości, które przechowywały te metody getterów i setterów i całkowicie je stracić! Coś, o co nie musisz się martwić w późniejszej metodzie.
Więc, możesz zobaczyć, dlaczego powiedziałem, że druga technika jest bardziej solidna.
Zapobieganie nadpisywaniu
Jeśli z jakiegoś powodu wolisz pierwszą technikę, uczyń właściwości przechowujące metody getter-setter tylko do odczytu, tworząc je za pomocą Object.defineProperties
. Właściwości utworzone za pomocą Object.defineProperties
, Object.defineProperty
i Reflect.defineProperty
automatycznie konfigurują się do writable: false
, co oznacza read-only:
/* 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"
Operacje wewnątrz getterów i setterów
Gdy już wprowadzisz gettery i settery, możesz iść dalej i wykonywać operacje na danych przed ich zmianą lub zwróceniem.
W poniższym kodzie w funkcji getter dane są konkatenowane z łańcuchem znaków przed ich zwróceniem, a w funkcji setter przed aktualizacją n
wykonywana jest walidacja, czy wartość jest liczbą, czy nie.
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"
Chroń dane za pomocą getterów i setterów
Do tej pory omówiliśmy użycie getterów i setterów w pierwszym kontekście enkapsulacji. Przejdźmy do drugiego, czyli jak ukryć dane przed kodem zewnętrznym za pomocą getterów i setterów.
Niechronione dane
Ustawienie getterów i setterów nie oznacza, że do danych można mieć dostęp i zmieniać je tylko za pomocą tych metod. W poniższym przykładzie dane są zmieniane bezpośrednio, bez dotykania metod getter i 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"
Nie użyliśmy settera, lecz bezpośrednio zmieniliśmy dane (fooVal
). Dane, które początkowo ustawiliśmy wewnątrz obj
teraz już nie istnieją! Aby zapobiec takim sytuacjom (przypadkowo), potrzebujesz pewnej ochrony dla swoich danych. Możesz dodać to poprzez ograniczenie zakresu, w którym twoje dane są dostępne. Możesz to zrobić za pomocą skalowania blokowego lub skalowania funkcji.
1. Skalowanie blokowe
Jednym ze sposobów jest użycie zakresu blokowego, wewnątrz którego dane zostaną zdefiniowane za pomocą słowa kluczowego let
, które ogranicza ich zakres do tego bloku.
Zakres blokowy można utworzyć, umieszczając swój kod wewnątrz pary nawiasów klamrowych. Kiedykolwiek tworzysz zakres blokowy, upewnij się, że zostawisz nad nim komentarz z prośbą o pozostawienie tych nawiasów w spokoju, aby nikt nie usunął ich przez pomyłkę, myśląc, że są to jakieś dodatkowe zbędne nawiasy w kodzie lub dodaj etykietę do zakresu blokowego.
/* 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"
Zmiana/tworzenie fooVal
poza blokiem nie wpłynie na fooVal
odniesione wewnątrz getterów setterów.
2. Function scoping
Najczęstszym sposobem ochrony danych za pomocą scopingu jest przechowywanie danych wewnątrz funkcji i zwracanie obiektu z getterami i setterami z tej funkcji.
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"
Obiekt (z foo()
getterem-setterem wewnątrz niego) zwrócony przez funkcję myobj()
jest zapisywany w obj
, a następnie obj
jest używany do wywoływania gettera i settera.
3. Ochrona danych bez scopingu
Jest też inny sposób, w jaki można chronić dane przed nadpisaniem bez ograniczania ich zakresu. Logika za tym idzie tak: jak możesz zmienić kawałek danych, jeśli nie wiesz, jak się nazywa?
Jeśli dane mają nie tak łatwo odtwarzalną nazwę zmiennej/właściwości, są szanse, że nikt (nawet my sami) nie skończy nadpisując je przez przypisanie jakiejś wartości do tej zmiennej/właściwości. Chociaż nazwa, którą wybrałem, nie jest naprawdę dobra, możesz również użyć losowych wartości lub symboli do tworzenia nazw właściwości, jak to proponuje Derick Bailey w tym poście na blogu. Głównym celem jest utrzymanie danych ukrytych przed innym kodem i pozwolenie parze getter-setter na dostęp/aktualizację.
Kiedy powinieneś używać getterów i seterów?
Teraz przychodzi wielkie pytanie: czy zaczynasz przypisywać gettery i setery do wszystkich swoich danych teraz?
Jeśli ukrywasz dane, to nie ma innego wyboru.
Ale jeśli twoje dane widziane przez inny kod są w porządku, to czy nadal musisz używać getterów i setterów tylko po to, aby powiązać je z kodem, który wykonuje na nich jakieś operacje? Powiedziałbym, że tak. Kod sumuje się bardzo szybko. Tworzenie mikrojednostek pojedynczych danych z własnym getterem-setterem daje ci pewną niezależność w pracy nad tymi danymi bez wpływu na inne części kodu.
Read Also: 10 Reasons Why You Need Code Optimization