Refleksja obiektów w JavaScript

Zgłębiamy temat

Poziom zaawansowany

Dziś możliwie krótko ale konkretnie, czyli mała refleksja o refleksji obiektów.

Refleksja obiektów w JavaScript

Refleksja obiektów jest nie tylko ciekawostką, ale i narzędziem z którym możemy się spotkać częściej niż sądzimy. I może ono się okazać tym, czego właśnie potrzebujemy jeżeli na czymś utkniemy.

Mechanizm refleksji pozwala tak pracować z kodem, jakby stanowił on dane. Oznacza to że możemy operować na kodzie tak jak na danych i tworzyć własne konstrukcje modyfikujące standardowe działanie języka.

Refleksja zwykle jest spotykana w językach wysokiego poziomu, najczęściej opartych na maszynie wirtualnej. Techniki takie zapewne są dobrze znane programistom takich języków jak Python czy Ruby.

Gdy zajdzie potrzeba, również w kodzie JavaScript możemy skorzystać z możliwości refleksji.

Podstawową rzeczą jest tutaj przegląd elementów, z których składa się interesujący nas obiekt. I tym się teraz zajmiemy.

Przegląd składowych obiektu

Taką możliwość zapewnia nam metoda Object.getOwnPropertyNames.

Przykład użycia na obiekcie Math:

alert(Object.getOwnPropertyNames(Math));

Wynik będzie mniej więcej taki:

[toSource,abs,acos,asin,atan,atan2,ceil,cos,exp,floor,imul,fround,log,max,min,pow,random,
round,sin,sqrt,tan,log10,log2,log1p,expm1,cosh,sinh,tanh,acosh,asinh,atanh,trunc,sign,cbrt,
E,LOG2E,LOG10E,LN2,LN10,PI,SQRT2,SQRT1_2]

Dzięki tylko jednej linijce kodu uzyskaliśmy naprawdę cenne informacje, które możemy dalej przetwarzać, w tym określić czy obiekt zawiera daną metodę lub właściwość (w implementacji JavaScriptu, na której jest wykonywany nasz kod).

To chyba najprostszy przykład elementów refleksji, jakie możemy zastosować w JavaScript. Prosty kod, który szybko wyświetla nam „ściągawkę” na temat tego, nad czym pracujemy.

Możemy oczywiście wypróbować to na innych obiektach, takich jak String, Number, naszych własnych. A co ważniejsze takich, które zostały opracowane przez innych programistów, są skompresowane, nieudokumentowane i… kto wie na co jeszcze może trafić programista. A dobrze jest być gotowym na wszystko.

Refleksja obiektów w JavaScript – krok dalej

Kolejną rzeczą, jaką możemy zrobić jest filtrowanie.

Napiszmy kod, który przetworzy obiekt i zwróci dostępne metody:

function getMethods(obj) {
    var props = Object.getOwnPropertyNames(obj);
    var methods = props.filter(function(property) {
        return typeof obj[property] == 'function';
    });
    
    return methods;
}

alert(getMethods(Math));

Rezultat, który zwrócił mi Firefox to:

[toSource,abs,acos,asin,atan,atan2,ceil,cos,exp,floor,imul,fround,log,max,min,pow,random,
round,sin,sqrt,tan,log10,log2,log1p,expm1,cosh,sinh,tanh,acosh,asinh,atanh,trunc,sign,cbrt]

Wystarczy zmienić linijkę:

...
return typeof obj[property] == 'function';
...

na

...
return typeof obj[property] != 'function';
...

a otrzymamy wszystko, co nie jest funkcją w danym obiekcie, np.:

E,LOG2E,LOG10E,LN2,LN10,PI,SQRT2,SQRT1_2

Przejdźmy teraz do metody pozwalającej określić, czy to czego szukamy faktycznie znajduje się w obiekcie.

Metoda hasOwnProperty

A konkretnie nasz_obiekt.hasOwnProperty(), zwraca wartość logiczną określającą czy dany obiekt posiada interesującą nas właściwość lub metodę.

Pierwszym z brzegu zastosowaniem jest zapobieganie błędom, to znaczy wpierw sprawdzamy czy dana metoda lub właściwość jest w ogóle dostępna, zanim się do niej odwołamy.

Używając nieco wyobraźni zobaczymy, że idąc tą drogą możemy uzyskać nowy poziom panowania nad naszym kodem.


Rozważmy przykład. Stworzymy obiekt i prześwietlimy go.


function getCarData() {

    var myCar = {
        name: "VW",
        model: "Bora",
        power_hp: 110,
        getColor: function () {
            // ...
        }
    };
    
    alert(myCar.hasOwnProperty('constructor')); // false
    alert(myCar.hasOwnProperty('getColor')); // true
    alert(myCar.hasOwnProperty('megaFooBarNonExisting')); // false

    if (myCar.hasOwnProperty('power_hp')) {
        alert(typeof(myCar.power_hp)); // number
    }
}

getCarData();

Operator typeof (zwracający typ argumentu) powinien być już dobrze znany Czytelnikowi. Zanim wyświetlimy typ elementu power_hp sprawdzamy, czy takowy w ogóle istnieje.

W przykładzie sprawdzamy także czy dostępne są metody; getColor jest dostępna, podczas gdy nie ma takiej jak megaFooBarNonExisting.

Na dziś to właściwie wszystko.

Podsumowanie

Można by tworzyć bardziej rozbudowane przykłady i dalej zagłębiać się w temat, jednak chodziło tutaj o zaprezentowanie Czytelnikowi mechanizmu refleksji i użycia go w JavaScript. Zachęcam do zgłębienia tego tematu również w kontekście innych języków programowania.

W każdym razie mam nadzieję, że skłoniłem kogoś do małej refleksji nad refleksją obiektów w JavaScript. Jest to coś, czego powiedzmy nie używa się na co dzień, a jednak coś o czym warto pamiętać.

Dziękuję za uwagę.

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem, początkujący bloger. Pasjonat programowania, nowych technologii, e-commerce, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook0Tweet about this on TwitterShare on Google+2Share on LinkedIn1Share on Tumblr0Digg thisEmail this to someonePin on Pinterest1
Możesz skomentować leave a response, lub podać trackback z własnej strony.