Symbole

Jedną z nowości jakie przyniosło ES6 jest nowy typ prymitywny zwany symbolem. Podobnie jak w przypadku innych prymitywów symbol możemy stworzyć za pomocą konstruktora, w przeciwieństwie jednak do innych typów nie możemy tutaj stosować literałów:


const sym = Symbol();
console.log(typeof sym); //symbol

//dla każdego symbolu możemy przekazać własną nazwę
//która ułatwia debugowanie
const sym2 = Symbol('Moj symbol');
console.log(sym2); //Symbol(Moj symbol)

Główną cechą symboli jest to, że każdorazowo przyjmują one całkowicie nową wartość, którą określa dany symbol. Nawet jeżeli przekażemy im taki sam tekst opisowy, każdy nowo utworzony symbol ma nową wartość, inną od innych symboli:


//false bo oba symbole mają podobny tekst opisowy, ale ich "wartości" są całkowicie unikatowe
console.log(Symbol("foo") === Symbol("foo"))

W przypadku Symboli nie interesuje nas jaką konkretną wartość ma dany symbol, ale to, że jego wartość jest zawsze unikatowa. Czyli w przeciwieństwie np. do Number odwołując się do zmiennej nie pobieramy konkretnej wartości, a tylko "coś unikatowego".

Do czego to się może przydać?

Wyobraź sobie, że wraz z innymi tworzycie super zaawansowany silnik do gier. Pojedynczy obiekt ma mnóstwo metod np collision, moved itp. Ty w pewnym momencie chcesz dodać nową metodę, ale też chcesz mieć pewność, że nie nadpiszesz żadnej innej metody. Klasycznie mógłbyś wykorzystać do tego celu pętlę while, tak by generować unikatową nazwę dla twojej metody:


let check = true;
let methodName = "moved";
const ob = {};

while (ob.hasOwnProperty(methodName)) {
    methodName += randomLetter();
}

ob[methodName] = function() {
    ...
}

W przypadku symboli zawsze będziesz miał unikatową wartość, a więc i twoją metodę:


const move = Symbol('my move method');

const ob = {};
ob[move] = function() {
    ...
}


//jedyna możliwość odwołania się do powyższej metody
//jest skorzystanie z symbolu, do którego mamy referencję w stałej move

ob[move](); //Test

const myObj = {};
let sym1 = Symbol('moj'); //tworze nowy symbol
myObj[sym1] = "Ala";

console.log(myObj); //{Symbol(moj): "Ala"}


sym1 = Symbol(); //tworze nowy symbol pod tą samą nazwą
myObj[sym1] = "Monika";

console.log(myObj); //{Symbol(moj): "Ala", Symbol(): "Monika"}

const sym2 = Symbol('moj'); //podobny opis nic nie oznacza
myObj[sym2] = "Beata";

console.log(myObj); //{Symbol(moj): "Ala", Symbol(): "Monika", Symbol(moj): "Beata"}

Nawet jeżeli stworzymy nowy symbol o tej samej nazwie i wartości, będzie on zupełnie nowym symbolem. Dzięki temu wszystkie trzy właściwości są pod innymi kluczami.

Jeżeli teraz chcielibyśmy się do tych właściwości dostać moglibyśmy użyć zmiennych wskazujących na dany symbol. W powyższym przykładzie jednak zmienna sym została zmieniona, więc w żaden sposób nie wskazuje na pierwszy symbol. Możemy tutaj skorzystać z metody Object.getOwnPropertySymbols(ob), która zwraca symbole użyte w danym obiekcie:


const symb = Object.getOwnPropertySymbols(myObj);
console.log(symb.length); //3
console.log(symb[0]); //Symbol(moj)
console.log(myObj[symb[0]]) //Ala

Metody dla symboli

JS udostępnia kilka metod dla symboli. Pierwszą z nich jest Symbol.for(nazwa). Służy ona do tworzenia danych symboli w globalnym rejestrze. Wyobraź sobie globalną listę. Gdy na danej pozycji nie ma jeszcze stworzonego symbolu, wtedy użycie tej metody stworzy pod daną pozycją nowy symbol. Każde kolejne użycie tej metody z daną nazwą nie stworzy nowego symbolu, a tylko pobierze już stworzony symbol:


Symbol.for("mySymbol name"); //tworzę nowy symbol
Symbol.for("other symbol"); //tworzę nowy symbol

console.log(Symbol.for("mySymbol name")); //pobieram wcześniej utworzony symbol
console.log(Symbol.for("mySymbol name2")); //tworzę nowy symbol

const hidden = Symbol.for("name"); //tworzę nowy symbol

//odwołuję się do symbolu
console.log(hidden);
console.log(Symbol.for("name"));

Jeżeli znamy nazwę zmiennej do której przypisany został symbol, możemy za jej pomocą pobrać klucz, który został wpisany przy tworzeniu symbolu. Wykorzystamy do tego metodę Symbol.keyFor()


const hidden = Symbol.for("my hidden message");
console.log(Symbol.keyFor(hidden)); //my hidden message