Symbole

> Dodaj do ulubionych

ES6 bez tajemnic to cykl artyku┼é├│w po┼Ťwi─Öconych nowym sk┼éadnikom j─Özyka JavaScript, kt├│re pojawi┼éy si─Ö w 6. edycji standardu ECMAScript, w skr├│cie ES6.

Czym s─ů symbole ES6?

Symbole to nie logo.

Nie s─ů to malutkie obrazki, kt├│re mo┼╝esz umie┼Ťci─ç w kodzie.

let ? = ? ├Ś ?;  // b┼é─ůd sk┼éadni

Symbol ES6 nie jest r├│wnie┼╝ ┼Ťrodkiem stylistycznym maj─ůcym ukryte znaczenie.

Czym wi─Öc s─ů symbole?

Si├│dmy typ

Od czasu ustandaryzowania JavaScriptu w 1997 r. w j─Özyku tym istnia┼éo sze┼Ť─ç typ├│w. Przed pojawieniem si─Ö standardu ES6 ka┼╝d─ů warto┼Ť─ç w programie JS mo┼╝na by┼éo zakwalifikowa─ç do kt├│rej┼Ť z poni┼╝szych kategorii:

  • undefined
  • null
  • typ logiczny (boolean)
  • liczba
  • ┼éa┼äcuch
  • obiekt

Ka┼╝dy typ reprezentuje zbi├│r warto┼Ťci. Pierwsza pi─ůtka to zbiory sko┼äczone. Istniej─ů oczywi┼Ťcie tylko dwie warto┼Ťci logiczne, true i false, i innych ju┼╝ nie b─Ödzie. Wi─Öcej jest natomiast warto┼Ťci liczbowych i ┼éa┼äcuchowych. Wed┼éug standardu istnieje 18.437.736.874.454.810.627 r├│┼╝nych warto┼Ťci liczbowych (┼é─ůcznie z NaN ÔÇô to skr├│t od angielskiego wyra┼╝enia Not a Number, oznaczaj─ůcy warto┼Ť─ç nieb─Öd─ůca liczb─ů). To jednak nic w por├│wnaniu z liczb─ů mo┼╝liwych ┼éa┼äcuch├│w. Wydaje mi si─Ö, ┼╝e to warto┼Ť─ç rz─Ödu (2144115188075855872 Ôłĺ 1) ├Ě 65.535ÔÇŽ chocia┼╝ mog┼éem pomyli─ç si─Ö w obliczeniach.

Warto┼Ťci obiektowe stanowi─ů natomiast zbi├│r otwarty. Ka┼╝dy obiekt jest unikalny niczym p┼éatek ┼Ťniegu. Przy ka┼╝dym otwarciu dowolnej strony internetowej tworzona jest masa nowych obiekt├│w.

Symbole ES6 s─ů warto┼Ťciami, lecz nie ┼éa┼äcuchami. Nie s─ů to tak┼╝e obiekty. To co┼Ť nowego: si├│dmy typ warto┼Ťci.

Pomówmy więc o sytuacji, w której mogłyby się nam przydać.

Jeden prosty typ logiczny

Czasem niezmiernie wygodnie by┼éoby m├│c przechowa─ç dodatkowe dane w obiekcie JavaScript nale┼╝─ůcym do kogo┼Ť innego.

Za┼é├│┼╝my na przyk┼éad, ┼╝e piszesz bibliotek─Ö JS, kt├│ra wykorzystuje efekty przej┼Ťcia CSS do poruszania element├│w struktury DOM po ekranie. Zauwa┼╝y┼ée┼Ť jednak, ┼╝e nie mo┼╝na chyba zastosowa─ç jednocze┼Ťnie kilku efekt├│w przej┼Ťcia na pojedynczym elemencie div ÔÇô powoduje to nieestetyczne, nieregularne przeskoki. Wydaje ci si─Ö, ┼╝e da si─Ö to naprawi─ç, ale najpierw trzeba w jaki┼Ť spos├│b sprawdzi─ç, czy dany element ju┼╝ si─Ö porusza.

Jak rozwi─ůza─ç taki problem?

Jednym sposobem jest wykorzystanie interfejs├│w API CSS, kt├│re wy┼Ťl─ů do przegl─ůdarki odpowiednie zapytanie. By┼éaby to chyba jednak ma┼éa przesada. Biblioteka powinna ju┼╝ wiedzie─ç czy element si─Ö porusza ÔÇô to jej kod wprawi┼é go w ruch!

Zale┼╝y nam tak naprawd─Ö na tym, by m├│c monitorowa─ç poruszaj─ůce si─Ö elementy. Mogliby┼Ťmy przechowywa─ç je w tablicy. Za ka┼╝dym razem, gdy wywo┼éamy bibliotek─Ö w celu animowania elementu, b─Ödziemy mogli tablic─Ö przeszuka─ç i sprawdzi─ç, czy dany element ju┼╝ si─Ö w niej znajduje.

Hmm. Je┼Ťli tablica jest du┼╝a, przeszukiwanie liniowe b─Ödzie wolne.

Tak naprawd─Ö wystarczy, by na elemencie ustawi─ç flag─Ö:

if (element.isMoving) {
  smoothAnimations(element);
}
element.isMoving = true;

Jednak i w tym przypadku mo┼╝emy napotka─ç potencjalne problemy, a wszystko dlatego, ┼╝e nasz kod nie korzysta ze struktury DOM jako jedyny.

  1. Na utworzon─ů przez nas w┼éasno┼Ť─ç mo┼╝e natrafi─ç inny kod, wykorzystuj─ůcy np. p─Ötl─Ö for-in lub metod─Ö Object.keys().
  2. Na pomys┼é wykorzystania tej techniki m├│g┼é wpa┼Ť─ç ju┼╝ inny tw├│rca biblioteki, przez co nasza biblioteka nie b─Ödzie z ni─ů dobrze wsp├│┼épracowa─ç.
  3. Na to samo rozwi─ůzanie mo┼╝e kiedy┼Ť wpa┼Ť─ç jeszcze inny tw├│rca, z kt├│rego bibliotek─ů nasza te┼╝ nie b─Ödzie dzia┼éa─ç prawid┼éowo.
  4. Komisja standaryzacyjna może postanowić o dodaniu metody .isMoving() do wszystkich elementów. To by był dopiero problem!

Oczywi┼Ťcie z ostatnich trzech sytuacji mo┼╝emy wybrn─ů─ç, wybieraj─ůc tak d┼éugi b─ůd┼║ niedorzeczny ┼éa┼äcuch, ┼╝e nikomu innemu podobna nazwa nie wpad┼éaby do g┼éowy:

if (element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) {
  smoothAnimations(element);
}
element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ = true;

Szkoda jednak psu─ç sobie wzrok.

Mo┼╝na wygenerowa─ç praktycznie unikatow─ů nazw─Ö w┼éasno┼Ťci z zastosowaniem technik kryptograficznych:

// wygeneruj 1024 bezsensowne znaki Unicode
var isMoving = SecureRandom.generateName();

...

if (element[isMoving]) {
  smoothAnimations(element);
}
element[isMoving] = true;

Sk┼éadnia obiekt[nazwa] pozwala wykorzysta─ç jako nazw─Ö w┼éasno┼Ťci dos┼éownie dowolny ┼éa┼äcuch. Zatem spos├│b ten jest obiecuj─ůcy: kolizje s─ů praktycznie niemo┼╝liwe, a kod wygl─ůda w porz─ůdku.

Pojawi─ů si─Ö jednak problemy przy debugowaniu. Wynik ka┼╝dorazowego wywo┼éania metody console.log() na elemencie o takiej w┼éasno┼Ťci b─Ödzie nieczytelny. Co je┼Ťli b─Ödziesz potrzebowa─ç wi─Öcej takich w┼éasno┼Ťci? Jak je ujednolici─ç? Po ka┼╝dym prze┼éadowaniu ich nazwa b─Ödzie si─Ö zmienia─ç.

Dlaczego to takie trudne? Potrzebujemy tylko jednego typu logicznego!

Rozwi─ůzaniem s─ů symbole

Symbole to warto┼Ťci, kt├│re programy mog─ů tworzy─ç i wykorzystywa─ç jako klucze w┼éasno┼Ťci bez ryzyka kolizji nazw.

var mySymbol = Symbol();

Wywo┼éanie metody Symbol() powoduje utworzenie nowego symbolu, warto┼Ťci wyj─ůtkowej spo┼Ťr├│d wszystkich warto┼Ťci.

Symbol mo┼╝e by─ç kluczem w┼éasno┼Ťci podobnie jak ┼éa┼äcuch czy liczba. Poniewa┼╝ jednak symbole s─ů inne od wszystkich ┼éa┼äcuch├│w, mamy pewno┼Ť─ç, ┼╝e w┼éasno┼Ť─ç o kluczu symbolowym nie b─Ödzie kolidowa┼éa z ┼╝adn─ů inn─ů w┼éasno┼Ťci─ů.

obj[mySymbol] = "ok!";  // na pewno nie spowoduje kolizji
console.log(obj[mySymbol]);  // ok!

Oto jak mo┼╝na zastosowa─ç symbole w opisanej powy┼╝ej sytuacji:

// utw├│rz unikatowy symbol
var isMoving = Symbol("isMoving");

...

if (element[isMoving]) {
  smoothAnimations(element);
}
element[isMoving] = true;

Kilka uwag do powy┼╝szego kodu:

  • ┼üa┼äcuch "isMoving" w wyra┼╝eniu Symbol("isMoving") nazywamy opisem. Opisy przydaj─ů si─Ö w debugowaniu. S─ů wy┼Ťwietlane podczas przesy┼éania symboli do konsoli za pomoc─ů metody console.log(), przekszta┼écania ich na typ ┼éa┼äcuchowy za pomoc─ů metody .toString(), a tak┼╝e mog─ů pojawi─ç si─Ö w komunikatach o b┼é─Ödach. To wszystko.
  • element[isMoving] nazywamy w┼éasno┼Ťci─ů o kluczu symbolowym. Jest to po prostu w┼éasno┼Ť─ç, kt├│rej nazw─ů nie jest ┼éa┼äcuch, lecz symbol. Poza tym nie r├│┼╝ni si─Ö pod ┼╝adnym wzgl─Ödem od innych zwyk┼éych w┼éasno┼Ťci.
  • Podobnie jak w przypadku element├│w tablicy, do w┼éasno┼Ťci o kluczu symbolowym nie mo┼╝na uzyska─ç dost─Öpu poprzez sk┼éadni─Ö kropkow─ů, np. obiekt.nazwa. W tym celu musimy skorzysta─ç z nawiasu kwadratowego.
  • Je┼Ťli natomiast mamy ju┼╝ nasz symbol, uzyskanie dost─Öpu do w┼éasno┼Ťci o kluczu symbolowym to pestka. Powy┼╝szy przyk┼éad pokazuje jak pobra─ç i ustawi─ç warto┼Ť─ç element[isMoving]. Mogliby┼Ťmy tak┼╝e zapyta─ç czy element si─Ö porusza za pomoc─ů instrukcji warunkowej if (isMoving in element), b─ůd┼║ w razie potrzeby usun─ů─ç go za pomoc─ů metody delete element[isMoving].
  • Z drugiej strony, wszystkie te operacje mo┼╝liwe s─ů jedynie wtedy, gdy symbol isMoving znajduje si─Ö w zakresie. Z tego powodu symbole mog─ů pos┼éu┼╝y─ç jako mechanizm s┼éabej hermetyzacji: modu┼é tworz─ůcy kilka w┼éasnych symboli mo┼╝e u┼╝y─ç ich na dowolnym obiekcie, bez ryzyka kolizji z w┼éasno┼Ťciami z innego kodu.

Poniewa┼╝ klucze symbolowe powsta┼éy z my┼Ťl─ů o zapobieganiu kolizjom, s─ů one ignorowane przez wi─Ökszo┼Ť─ç g┼é├│wnych narz─Ödzi JavaScript do inspekcji obiekt├│w. Przyk┼éadowo p─Ötla for-in przegl─ůda tylko ┼éa┼äcuchowe klucze obiektu. Klucze symbole s─ů pomijane. Podobnie jest w przypadku metod Object.keys(obj) i Object.getOwnPropertyNames(obj) . Symbole nie s─ů jednak w zupe┼éno┼Ťci prywatne: korzystaj─ůc z nowego interfejsu API Object.getOwnPropertySymbols(obj) mo┼╝emy wygenerowa─ç list─Ö kluczy symbolowych obiektu. Inne nowe API, Reflect.ownKeys(obj) , zwraca zar├│wno klucze ┼éa┼äcuchowe jak i symbolowe. (API Reflect om├│wimy wyczerpuj─ůco w kolejnym artykule).

Symbole znajd─ů szereg zastosowa┼ä w bibliotekach i systemach szkieletowych, lecz i sam j─Özyk, jak si─Ö niebawem przekonamy, wykorzystuje je do wielu cel├│w.

Czym jednak dok┼éadnie s─ů symbole

> typeof Symbol()
"symbol"

Symboli nie da się tak zupełnie porównać z niczym innym.

Po utworzeniu staj─ů si─Ö one niemodyfikowalne. Nie mo┼╝na im nada─ç w┼éasno┼Ťci (w trybie ┼Ťcis┼éym zostanie zwr├│cony b┼é─ůd typu). Mo┼╝na je wykorzysta─ç jako nazwy w┼éasno┼Ťci. To wszystko cechy typowe dla ┼éa┼äcuch├│w.

Z drugiej jednak strony ka┼╝dy symbol jest niepowtarzalny, r├│┼╝ny od wszystkich innych (nawet tych maj─ůcych taki sam opis), a tworzenie nowych symboli nie sprawia k┼éopot├│w. To z kolei cechy charakteryzuj─ůce obiekty.

Symbole ES6 przypominaj─ů bardziej tradycyjne symbole z j─Özyk├│w Lisp czy Ruby, lecz nie s─ů one zintegrowane z j─Özykiem w tak du┼╝ym stopniu. W j─Özyku Lisp wszystkie identyfikatory to symbole, za┼Ť w JS identyfikatory i wi─Ökszo┼Ť─ç kluczy w┼éasno┼Ťci traktowanych jest jako ┼éa┼äcuchy. Symbole s─ů tylko dodatkow─ů opcj─ů.

Jedna uwaga: symboli, w przeciwie┼ästwie do praktycznie wszystkich pozosta┼éych element├│w JS, nie mo┼╝na automatycznie przekszta┼éci─ç na ┼éa┼äcuchy. Pr├│ba po┼é─ůczenia symbolu z ┼éa┼äcuchem wywo┼éa b┼é─ůd typu.

> var sym = Symbol("<3");
> "tw├│j symbol to " + sym
// TypeError: can't convert symbol to string
> `tw├│j symbol to ${sym}`
// TypeError: can't convert symbol to string

B┼é─Ödu tego mo┼╝na unikn─ů─ç, zamieniaj─ůc symbol na ┼éa┼äcuch z zastosowaniem jawnej konwersji String(sym) lub metody sym.toString().

Trzy zestawy symboli

Istniej─ů trzy sposoby na uzyskanie symbolu.

  • Wywo┼éanie metody Symbol(). Jak ju┼╝ powiedzieli┼Ťmy, metoda ta za ka┼╝dym razem zwraca nowy, unikatowy symbol.
  • Wywo┼éanie metody Symbol.for(string) . W ten spos├│b otrzymujemy dost─Öp do zbioru istniej─ůcych ju┼╝ symboli, tzw. rejestru symboli. W przeciwie┼ästwie do unikatowych symboli definiowanych przez metod─Ö Symbol(), symbole w rejestrze s─ů wsp├│┼édzielone. Je┼Ťli wi─Öc wywo┼éasz metod─Ö Symbol.for("kot") trzydzie┼Ťci razy, to za ka┼╝dym razem zostanie zwr├│cony ten sam symbol. Rejestr przydaje si─Ö, kiedy wiele stron internetowych lub wiele modu┼é├│w jednej strony musi wsp├│┼édzieli─ç jaki┼Ť symbol.
  • Skorzystanie z symboli zdefiniowanych w standardzie, np. Symbol.iterator. Kilka symboli jest zdefiniowanych w samym standardzie, a ka┼╝dy z nich ma specjaln─ů funkcj─Ö.

Je┼Ťli wci─ů┼╝ nie jeste┼Ť przekonany czy symbole s─ů rzeczywi┼Ťcie u┼╝yteczne, ostatnia kategoria jest ciekawa, poniewa┼╝ znajdziesz w niej autentyczne przyk┼éady zastosowa┼ä symboli w praktyce.

Jak standard ES6 wykorzystuje znane symbole

Poznali┼Ťmy ju┼╝ jeden spos├│b, w jaki ES6 korzysta z symboli ÔÇô s┼éu┼╝─ů one unikni─Öciu konflikt├│w z istniej─ůcym kodem. Jaki┼Ť czas temu, w artykule na temat iterator├│w, zobaczyli┼Ťmy, ┼╝e p─Ötla for (var item of myArray) rozpoczyna si─Ö wywo┼éaniem metody myArray[Symbol.iterator]() . Wspomnia┼éem, ┼╝e metoda ta mog┼éaby nosi─ç nazw─Ö myArray.iterator(), jednak w celu zapewnienia zgodno┼Ťci wstecznej lepiej pos┼éu┼╝y─ç si─Ö symbolem.

┼üatwo to teraz zrozumie─ç skoro wiemy ju┼╝, jak dzia┼éaj─ů symbole.

Oto kilka innych zastosowa┼ä znanych symboli w ES6. (Poni┼╝sze elementy funkcjonalno┼Ťci nie zosta┼éy jeszcze zaimplementowane w Firefoksie).

  • Mo┼╝liwo┼Ť─ç rozszerzenia operatora instanceof. W ES6 wyra┼╝enie obiekt instanceof konstruktor zdefiniowane jest jako metoda konstruktora: konstruktor[Symbol.hasInstance](obiekt). Oznacza to, ┼╝e jest ono rozszerzalne.
  • Wyeliminowanie konflikt├│w pomi─Ödzy nowymi sk┼éadnikami a starym kodem. Przyczyny tego zjawiska s─ů bardzo niejasne, jednak ustalili┼Ťmy ┼╝e sama obecno┼Ť─ç niekt├│rych metod tablicowych ES6 na istniej─ůcych stronach internetowych powoduje ich awari─Ö. W innych standardach sieciowych wyst─Öpowa┼éy podobne problemy: wystarczy┼éo doda─ç w przegl─ůdarce nowe metody, by istniej─ůce strony uleg┼éy awarii. W g┼é├│wnej mierze by┼éo to jednak spowodowane tzw. dynamicznym okre┼Ťlaniem zakresu. W ES6 wprowadzono wi─Öc specjalny symbol Symbol.unscopables, kt├│ry mo┼╝e by─ç wykorzystywany przez standardy sieciowe, by niekt├│re metody nie by┼éy brane pod uwag─Ö w dynamicznym okre┼Ťlaniu zakresu.
  • Obs┼éugiwanie nowych rodzaj├│w dopasowywania ┼éa┼äcuch├│w. W ES5 metoda str.match(mojObiekt) od razu pr├│bowa┼éa przekonwertowa─ç mojObiekt na obiekt RegExp, za┼Ť w ES6 najpierw sprawdza, czy mojObiekt ma metod─Ö mojObiekt[Symbol.match](str) . Obecnie, za po┼Ťrednictwem bibliotek, mo┼╝na korzysta─ç z w┼éasnych klas parsuj─ůcych ┼éa┼äcuchy, kt├│re dzia┼éaj─ů wsz─Ödzie tam, gdzie dzia┼éaj─ů obiekty RegExp.

Ka┼╝de z wymienionych zastosowa┼ä jest dosy─ç w─ůskie i nie wydaje mi si─Ö, by kt├│rykolwiek z tych element├│w funkcjonalno┼Ťci mia┼é znacz─ůcy wp┼éyw na pisany przeze mnie na co dzie┼ä kod. W szerszej perspektywie s─ů one jednak bardziej interesuj─ůce. Znane symbole w JS to udoskonalona wersja __podwojnegoPodkreslenia z PHP i Pythona. W przysz┼éo┼Ťci stan─ů si─Ö one cz─Ö┼Ťci─ů standardu, by umo┼╝liwi─ç dodawanie do j─Özyka nowych punkt├│w zaczepienia bez zak┼é├│cania dzia┼éania istniej─ůcego kodu.

Kiedy b─Öd─Ö m├│g┼é zacz─ů─ç korzysta─ç z symboli ES6?

Symbole s─ů zaimplementowane w 36. wersji Firefoksa i 38. wersji przegl─ůdarki Chrome. W Firefoksie zaimplementowa┼éem je ja, zatem wiesz kogo szuka─ç w razie k┼éopot├│w.

By m├│c korzysta─ç z symboli w przegl─ůdarkach, kt├│re nie obs┼éuguj─ů standardowo tych sk┼éadnik├│w ES6, mo┼╝esz u┼╝y─ç wype┼éniacza, np. core.js. Poniewa┼╝ symbole nie przypominaj─ů w stu procentach ┼╝adnego istniej─ůcego ju┼╝ elementu funkcjonalno┼Ťci JS, wype┼éniacz ten nie jest idealny. Informacje dotycz─ůce jego ogranicze┼ä znajduj─ů si─Ö tutaj.

W nast─Öpnej ods┼éonie serii przedstawimy dwa nowe artyku┼éy. Na pocz─ůtek om├│wimy kilka d┼éugo wyczekiwanych sk┼éadnik├│w, kt├│re wreszcie stan─ů si─Ö cz─Ö┼Ťci─ů JavaScriptu w ES6 ÔÇô i troch─Ö sobie na nie ponarzekamy. Zaczniemy od dw├│ch element├│w, kt├│rych historia si─Öga niemal┼╝e samych pocz─ůtk├│w programowania. Nast─Öpnie powiemy o dw├│ch bardzo podobnych sk┼éadnikach, kt├│re jednak oparte s─ů na efemerach (ang. ephemerons). Odwiedzaj nas zatem cz─Östo, by szczeg├│┼éowo pozna─ç kolekcje w ES6.

Autor: Jason Orendorff

Źródło: https://hacks.mozilla.org/2015/06/es6-in-depth-symbols/

Tłumaczenie: Joanna Liana

Tre┼Ť─ç tej strony jest dost─Öpna na zasadach licencji CC BY-SA 3.0

2 komentarze do “Symbole”

  1. Heh, czyli taki GUID w JS? Cho─ç GUID nie jest do ko┼äca unikalny… Ale w ramach jednej aplikacji JS Symbol jest zapewne w pe┼éni unikalny ­čÖé
    Fajne, Dzi─Öki!

Mo┼╝liwo┼Ť─ç komentowania zosta┼éa wy┼é─ůczona.