23 grudnia 2013
Po większej części, w JavaScript, to co widzisz jest tym, co dostajesz. Wartość jest wartością; nie ma żadnych sztuczek. Czasami jednak chcesz wartość, która jest oparta na innych wartościach: na przykład czyjeś pełne imię i nazwisko jest konkatenacją imienia i nazwiska. Jeśli masz obiekt person
i chcesz, aby użytkownicy tego obiektu mogli ustawić pełne, pierwsze lub ostatnie imię i zobaczyć, że zmiana ta jest natychmiast odzwierciedlona w innych wartościach, konwencjonalnie zbudowałbyś go za pomocą funkcji:
person.setLastName('Smith');person.setFirstName('Jimmy');person.getFullName(); // Jimmy Smith
Ale jest to brzydkie i wymaga od użytkowników twojego obiektu dbania o to, aby właściwości były powiązane; w bardziej złożonym przykładzie, może to nie być tak oczywiste, jak w przypadku nazw. Na szczęście istnieje lepszy sposób, dodany w ECMAScript 5.
Meet getters and setters.
How
Zróbmy obiekt osoby. Chcemy mieć możliwość ustawienia imienia, nazwiska lub pełnej nazwy i automatycznej aktualizacji dwóch pozostałych.
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
Więc co się tutaj dzieje?
Słowa kluczowe get i set są ważne. Następuje po nich właściwość, do której się odnoszą (fullName
), oraz ciało funkcji, które określa zachowanie, gdy właściwość jest dostępna (name = person.fullName
) lub modyfikowana (person.fullName = 'Some Name'
).
Te dwa słowa kluczowe definiują funkcje dostępowe: getter i setter dla właściwości fullName
. Gdy właściwość jest dostępna, używana jest wartość zwracana przez getter. Kiedy wartość jest ustawiana, wywoływany jest setter i przekazywana jest wartość, która została ustawiona. Od ciebie zależy, co zrobisz z tą wartością, ale to, co jest zwracane z settera, to wartość, która została przekazana – więc nie musisz niczego zwracać.
Oficjalny sposób: Object.defineProperty
Wraz z metodą inline deklarowania getterów i setterów, można to również zrobić bardziej jawnie poprzez Object.defineProperty
(MDN Documentation). Metoda ta przyjmuje trzy argumenty. Pierwszym z nich jest obiekt, do którego ma zostać dodana właściwość, drugim jest nazwa właściwości, a trzecim obiekt opisujący właściwość (znany jako deskryptor właściwości). Oto przykład, który replikuje powyższy przykład:
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 || '';}});
Zaleta nie jest tu od razu widoczna. Poza możliwością dodawania właściwości po utworzeniu obiektu początkowego, czy jest jakaś realna korzyść?
Gdy zdefiniujesz właściwość w ten sposób, możesz zrobić znacznie więcej niż tylko zdefiniować setter lub getter. Możesz również przekazać następujące klucze:
-
configurable
(false
domyślnie): jeśli jest to prawda, konfiguracja właściwości będzie modyfikowalna w przyszłości. -
enumerable
(false
domyślnie): jeśli prawda, właściwość pojawi się podczas pętli nad obiektem (for (var key in obj)
).
Możemy również zdefiniować właściwości, które nie mają jawnych getterów i setterów:
Object.defineProperty(person, 'age', { value: 42});
To utworzy person.age
i ustawi ją na wartość 42. Ważne jest, aby zauważyć, że ta właściwość nie jest zapisywalna. Wywołanie person.age = 99
nie będzie miało żadnego efektu. W ten sposób możesz tworzyć właściwości tylko do odczytu. Jeśli właściwość ma ustawiony klucz value
, to nie może mieć gettera ani settera. Właściwości mogą mieć wartości lub accessory, ale nie oba.
Nie tylko to, ale ponieważ właściwość enumerable
jest domyślnie ustawiona na false
, ta właściwość nie pojawi się, gdy wykonamy pętlę nad kluczami obiektu.
Jeżeli chcielibyśmy uczynić właściwość zapisywalną, musielibyśmy ustawić właściwość writable
:
Object.defineProperty(person, 'age', { value: 42, writable: true});
Teraz, person.age = 99;
przyniesie pożądany efekt.
Nadużywanie
Pamiętaj: tylko dlatego, że dana funkcja istnieje, nie musi być używana cały czas. Gettery i Settery mają swoje przypadki użycia, ale nie przesadzaj, bo najprawdopodobniej skończysz z projektem, który jest mylący dla tych, którzy wchodzą w interakcję z twoimi obiektami. Używane ostrożnie, są bardzo potężne. Ale z wielką mocą przychodzi wielka odpowiedzialność.
Pomoc dla przeglądarek?
IE9 i wyższe mają pełną obsługę Object.defineProperty
, wraz z Safari 5+, Firefox 4+, Chrome 5+ i Opera 12+. Jeśli pracujesz z Node.js, masz pełne wsparcie. Czyż nie kochasz Node’a?!
Ten artykuł został napisany wspólnie z Tomem Ashworthem. Podziękowania dla Toma za pomoc w złożeniu tego razem.
Jeśli podobał Ci się ten post, dołącz do newslettera, aby otrzymywać aktualizacje o nowej zawartości 😎.
Wszystkie wpisy na blogu
Jeśli podobał Ci się ten post, koniecznie śledź mnie na Twitterze.
.