Ta strona używa ciasteczek (cookies), dzięki którym nasz serwis może działać lepiej. Dowiedz się więcej OK, rozumiem
WebHelp.pl Warsztat Artykuły Typy danych, wartości i konwersja typów w JavaScript

Warsztat / Artykuły i tutoriale

Typy danych, wartości i konwersja typów w JavaScript

Rafał Kukawski 7 listopada 2014 komentarze ()

Tagi:JavaScript

Nawiązując nieco do poprzedniego artykułu o zmiennych (i funkcjach), tym razem omówimy typy danych w JavaScript.

JavaScript operuje na danych sześciu wbudowanych typów - Boolean, Number, String, Undefined, Null oraz Object.

Pierwsze pięć typów zaliczamy do tzw. typów prymitywnych.

Typ logiczny

W przypadku typu logicznego (Boolean) nie ma żadnej głębszej filozofii. Typ ten definiuje dwie wartości – logiczną prawdę (true) oraz logiczny fałsz (false). I to właśnie tych słów kluczowych będziemy używać w kodzie.

Kod: Zaznacz cały
var prawda = true,
    fałsz = false;

Typy Undefined i Null

Jedynymi wartościami tych typów są kolejno undefined oraz null.

Typ undefined oznacza po prostu typ niezdefiniowany. Jest on używany zarówno do oznaczenia braku wartości jak i wartości niezdefiniowanej.

Kod: Zaznacz cały
var arr = [undefined];

console.log(arr[0], arr[1]); // undefined, undefined

W powyższym przykładzie tworzymy tablicę z jedną wartością – undefined. Następnie odczytujemy pierwszą i drugą wartość tablicy. Zgodnie z oczekiwaniami, pierwsza zalogowana wartość to undefined. Gdy odczytujemy drugą wartość jedno-elementowej tablicy, też dostaniemy undefined.

null, podobnie jak w innych językach programowania, oznacza nic. W zasadzie może przypominać przeznaczeniem undefined, ale null został pomyślany raczej jako wyznacznik braku referencji do obiektu. W praktyce, z null spotkamy się używając funkcji wyszukujących element w dokumencie, np.

Kod: Zaznacz cały
var element = document.getElementById("id_elementu");

if (element !== null) {
    // logika programu
}

Gdy żaden element spełniający podane kryteria (np. atrybut ID o danej wartości) nie zostanie znaleziony, funkcja zwróci null.

Typ liczbowy

JavaScript posiada tylko jeden typ liczbowy - Number. Nie ma rozróżnienia na typ liczb całkowitych i ułamkowych, jak ma to miejsce w wielu innych językach. W JavaScript liczby traktowane są wg standardu IEEE 754 dla liczb zmiennoprzecinkowych podwójnej precyzji.

Kod: Zaznacz cały
var jeden = 1;
var pi = 3.14;
var minusDwa = -2;

Typ liczbowy, oprócz typowych liczb, definiuje też 3 wartości specjalne – not a number, +∞ oraz -∞. Takie wartości zdefiniowane są we wspomnianym standardzie IEEE 754 i zostały udostępnione w JavaScripcie.

NaN (not a number)

not a number oznacza, że dana wartość, wynik operacji, nie jest liczbą. Z wartością tą spotkamy się w różnych sytuacjach. Przykładowo, gdy podzielimy zero przez zero.

Kod: Zaznacz cały
var wynik = 0 / 0;

console.log(wynik); // NaN

Oczywiście w programach staramy się nie dzielić przez zero, ale nie oznacza to, że NaN będzie nam obce podczas kodowania skryptów. Najczęściej na NaN trafimy podczas nieudanej konwersji danych do typu liczbowego. Ale o konwersji będzie w dalszej części artykułu.

W każdym razie, wartość not a number jest dostępna w JavaScript pod globalną nazwą NaN oraz jako własność konstruktora Number – Number.NaN.

NaN posiada jeszcze jedną cechę w JavaScript, która może wydawać się dość absurdalna. Co jeśli chcielibyśmy sprawdzić, czy zmienna posiada wartość NaN? Prawdopodobnie Twoją pierwszą myślą jest:

Kod: Zaznacz cały
if (zmienna === NaN) {
    // logika, gdy zmienna jest not a number
}

Niestety, ten kod nie zadziała. NaN jest jedyną wartością w JavaScript, której nie można porównać ze sobą. Próba porównania NaN z czymkolwiek ma zdefiniowany wynik false. Jak zatem sprawdzić, czy coś jest NaN? Do tego celu mamy wbudowaną, globalną funkcję isNaN.

Kod: Zaznacz cały
if (isNaN(zmienna)) {
    // logika, gdy zmienna jest not a number
}

Niestety, funkcja ta robi coś więcej niż tylko sprawdzenie, czy jej parametr ma wartość NaN. Funkcja ta próbuje rzutować swój parametr do typu liczbowego i dopiero po tym sprawdza, czy wartość jest NaN. Poniżej przykład dwóch tekstów.

Kod: Zaznacz cały
console.log(
    isNaN("foo"), // true
    isNaN("3.14") // false
);

Ostatecznie, jeśli interesuje nas rozpoznanie NaN, można wykorzystać wspomniany wyżej fakt, że NaN nie jest równy NaN, a wartości wszystkich innych typów dają się porównywać.

Kod: Zaznacz cały
var value = NaN;

if (value !== value) {
    // console.log("Mamy NaN");
}

Niestety, czytelność takiego rozwiązania pozostawia wiele do życzenia pod kątem zrozumienia intencji programisty, a użycie isNaN i tak będzie wystarczające dla zdecydowanej większości zastosowań.

Nieskończoność

Wartość nieskończoności – jak sama nazwa mówi oznacza matematyczną nieskończoność. Wartość tę można uzyskać poprzez dzielenie dowolnej, niezerowej liczby przez zero czy też przekazując odpowiednie wartości parametrów dla różnych funkcji obiektu Math.

Kod: Zaznacz cały
console.log(1 / 0, -1 / 0);

Wartość tą znajdziemy też pod globalną nazwą Infinity oraz pod własnościami obiektu Number – Number.POSITIVE_INFINITY oraz Number.NEGATIVE_INFINITY.

Nieskończoność raczej nie często się przyda, ale żeby sprawdzić, czy dana wartość jest liczbą skończoną dowolnego znaku, możemy użyć wbudowanej funkcji isFinite, choć Infinity, w przeciwieństwie do NaN, jest wartością do której można się porównywać.

Kod: Zaznacz cały
var inf = 1 / 0;
console.log(isFinite(3.14), isFinite(inf), isFinite(-inf), inf === Infinity);

Zapis liczb

Dyskutując o liczbach, warto też zapoznać się z możliwymi formatami zapisu liczb w JavaScript. Wyżej już można było zobaczyć zapis liczb całkowitych i ułamkowych, np. 0, 1, -2, 3.0, 3.14, -6.28. W skrócie, opcjonalnie podajemy znak liczby, potem część całkowitą i opcjonalnie część ułamkową oddzieloną znakiem kropki.

JavaScript pozwala też zapisywać liczby systemem szesnastkowym. Zapis takiej liczby rozpoczynamy od 0x lub 0X, po czym piszemy sekwencję znaków 0-9a-fA-F, np. 0x0, 0X1, 0xFF, -0xAB.

Ponadto, możemy liczby zapisywać notacją wykładniczą (naukową), np. 1e3, 314e-2, 2.718e0. Zapis naukowy rozszerza standardową notację o część zawierającą e lub E oraz liczbę całkowitą będącą wykładnikiem (z opcjonalnym znakiem + lub -). 1e3 przykładowo jest równoznaczne z 1 * 103, co jest równe tysiącowi.

JavaScript oferuje jeszcze literał liczbowy w systemie ósemkowym, ale tryb ścisły zabrania tej notacji, dlatego nie będę poświęcał jej w ogóle uwagi.

Typ tekstowy

Wartość typu tekstowego jest sekwencją zera lub więcej znaków umieszczonych pomiędzy dwoma cudzysłowami lub apostrofami.

Kod: Zaznacz cały
var pusty = "";
var przykład = 'Lorem ipsum';

Większość znaków możemy zapisywać dosłownie (jak w powyższym przykładzie litery składające się na słowa „Lorem ipsum”). Każdy znak można zapisać w formie sekwencji ucieczki (escape sequence).

JS Bin

Powyższy przykład pokazuje zapis litery „A” dosłownie, za pomocą ucieczki systemem szesnastkowym oraz unikodowym. Wszystkie 3 sposoby definiują ten sam znak, co możemy udowodnić prostym testem.

JS Bin

Ucieczka notacją szesnastkową bazuje na znakach odwrotnego ukośnika, małej litery „x” oraz dwóch znaków (0-9a-fA-F) składających się na liczbę w systemie szesnastkowym.

Ucieczka notacją unikodową bazuje na znakach odwrotnego ukośnika, małej litery „u” oraz czterech znakach składających się na liczbę w systemie szesnastkowym (stąd 41 jest poprzedzone dwoma zerami).

Kilka znaków nie może wystąpić dosłownie w literale tekstowym, dlatego JavaScript definiuje specjalne sekwencje ucieczki. Są to:

JS Bin

które odpowiadają kodom

JS Bin

Ponadto, jeśli literał tekstowy zaczęliśmy od cudzysłowu, to ten znak też nie może wystąpić dosłownie w tekście, tylko musi zostać poprzedzony backslashem. Analogicznie sprawa wygląda dla literałów rozpoczętych apostrofem.

JS Bin

Co siedzi w środku?

Sekwencje ucieczki mogą nieco podpowiadać, że typ tekstowy w JavaScripcie bazuje na Unikodzie. Tak właśnie jest, każdy znak w tekście jest traktowany jako jednostka kodowa UTF-16. Należy jednak nadmienić, że w specyfikacji użyto pewnego uproszczenia – typ tekstowy jest zdefiniowany jako uporządkowana sekwencja 16-bitowych liczb całkowitych bez znaku. Dzięki temu uproszczeniu zyskujemy większą wydajność w przetwarzaniu tekstów przez wbudowane funkcje tekstowe. Dla przykładu, własność length obiektu tekstowego musi zliczyć tylko ilość 16-bitowych elementów w sekwencji.

JS Bin

Uproszczenie to ma jednak pewne konsekwencje. Standard Unicode przewiduje miejsce na 1114112 znaków, czyli przedział kodów od U+0000 do U+10FFFF. Na 16 bitach zmieści się „zaledwie” przedział U+0000 do U+FFFF. Żeby móc zapisywać znaki spoza pierwszego przedziału (zwanego BMP, Basic Multilingual Plane), kodowanie UTF-16 stosuje tzw. pary zastępcze (surrogate pairs), tj. taki znak zostanie zapisany jako dwa 16-bitowe kody (elementy). Dla JavaScriptu te dwa kody są niezależne, w ogóle ze sobą nie powiązane.

JS Bin

Niestety, sytuacja ta generuje spore zamieszanie, ponieważ ludzie operują na widzialnych symbolach (np. jeśli mamy odpowiednią przeglądarkę zobaczymy jedną emotkę w powyższym przykładzie), a JavaScript próbuje nam wmówić, że mamy do czynienia z dwoma znakami…

JS Bin

Brak powiązania pomiędzy kodami prowadzi też do sytuacji, że przetworzony tekst może nie być prawidłowym tekstem w kodowaniu UTF-16.

JS Bin

Ponadto proszę jeszcze raz spojrzeć na zapis sekwencji ucieczki. Wersja szesnastkowa pozwala zapisywać tylko 256 kodów (U+0000 do U+00FF), ponieważ do dyspozycji mamy tylko dwie „cyfry szestnastkowe”, zaś wersja unikodowa pozwala zapisać tylko 65536 kodów (U+0000 do U+FFFF) ze względu na udostępnione tylko 4 „cyfry szesnastkowe” w zapisie sekwencji ucieczki.

Pocieszeniem w całym tym zamieszaniu może być fakt, że większość stosowanych przez ludzi znaków znajduje się w pierwszym przedziale kodowym (U+0000 do U+FFFF, tzw. BMP), dlatego z opisanymi tutaj sytuacjami powinniśmy mieć względnie rzadko do czynienia.

Tutaj trzeba wspomnieć, że emotki wbudowane w klawiatury na smartfonach bazują w wielu przypadkach właśnie na Unikodzie zamiast na obrazkach, m.in. na przedziale kodowym U+1F600 do U+1F64F.

Typ tablicowy?

Może się nasuwać pytanie, co z tablicami w JavaScript? Czy stanowią one osobny typ? Otóż nie. W JavaScript tablice są obiektami z prototypem definiującym typowo tablicowe operacje. Nieco więcej o tablicach będzie w dalszej części artykułu.

Sprawdzanie typu

OK, poznaliśmy wszystkie dostępne typy danych w JS. Warto teraz wiedzieć, jak sprawdzić jakiego typu jest dana wartość. Do tego zadania przyda się operator typeof, który zwraca wartość tekstową reprezentującą rozponany typ.

Kod: Zaznacz cały
typeof wartość;

W takim razie zobaczmy typowe wartości:

Kod: Zaznacz cały
console.log(
    typeof false, // "boolean"
    typeof true, // "boolean"
    typeof 1, // "number"
    typeof 3.14, // "number"
    typeof "foo", // "string"
    typeof 'bar', // "string"
    typeof undefined, // "undefined"
    typeof {}, // "object"
    typeof [], // "object"

    typeof null // "object"
);

Wszystkie wartości z wyjątkiem ostatniej są raczej oczekiwanym wynikiem. Zagadką pozostaje wartość null meldująca się typem obiektowym. Niestety, w taki sposób właśnie zostało zdefiniowane w specyfikacji języka działanie operatora typeof. Dzisiaj jest to uznawane za wadę i padła nawet propozycja zmiany zachowania operatora w przyszłych wersjach języka, jednak pomysł został odrzucony, ponieważ taka - w teorii drobna zmiana - spowodowałaby zepsucie wielu istniejących skryptów, co jest przypadkiem sprzecznym z filozofią rozwoju języka.

Proszę zauważyć też zwrócone oznaczenie typu dla tablicy. Jak już zostało wyjaśnione w poprzednich rozdziałach, tablice w JS nie definiują swojego własnego typu, tylko są wyspecjalizowanymi obiektami.

W powyższym teście pominąłem kilka wartości. Przykładowo NaN i nieskończoności.

Kod: Zaznacz cały
console.log(
    typeof NaN, // "number"
    typeof Infinity, // "number"
    typeof -Infinity // "number"
);

W przypadku tych trzech wartości, typeof informuje, że mamy do czynienia z typem liczbowym. Dla niektórych sytuacja ta (szczególnie NaN) może przypominać przypadek null, ale jednak uzasadnieniem takiego rezultatu jest fakt, że NaN czy nieskończoności są po prostu specjalną sekwencją bitów, którą standard IEEE754 nakazuje traktować jako NaN lub nieskończoność.

Typ funkcyjny?

Tabela definiująca zachowanie operatora typeof wymienia jeszcze jeden typ – typ funkcyjny. Do tej pory nie wymienialiśmy w ogóle typu funkcyjnego. I prawidłowo, ponieważ JavaScript nie posiada takiego typu, tylko operator typeof w ramach wyjątku wyróżnia obiekty funkcyjne, co jest przydatną cechą tego operatora. Analiza, czy dany obiekt jest funkcją polega na sprawdzeniu pewnych wewnętrznych cech obiektu, do których my, jako programiści JavaScriptu nie mamy dostępu.

Rozpoznawanie tablic

Warto jeszcze rozpoznać, czy dana wartość jest tablicą. Jak już wiemy, tablice są typu obiektowego. Tutaj przyda się użycie innego operatora – instanceof.

Kod: Zaznacz cały
console.log([] instanceof Array); // true

I to by było na tyle, gdyby nie jeden problem, który napotkają programiści aplikacji, które przekazują sobie dane pomiędzy dokumentami (np. ramkami). Okazuje się, że przeglądarki tworzą dla każdego dokumentu „nowy zestaw” konstruktorów, m.in. Array. Dlatego obiekty z poszczególnych ramek nie dzielą tych samych konstruktorów, dlatego operator instanceof nie ma prawa zadziałać prawidłowo na obcych obiektach. Gdy konieczne jest przekazywanie danych pomiędzy dokumentami, warto rozważyć serializację danych (np. do formatu JSON) i deserializowanie po „drugiej stronie”. Na wypadek, gdyby istniały powody przeciwko serializacji, ostatnie wydanie specyfikacji języka dodaje funkcję Array.isArray, która rozwiązuje problem.

Kod: Zaznacz cały
console.log(Array.isArray([])); // true

Dzisiaj jest trudno znaleźć przeglądarkę ze znacznym udziałem na rynku, która tej funkcji by nie miała, ale gdybyś akurat musiał pracować ze starymi browserami, poniżej przedstawiam jak sobie poradzić bez funkcji isArray.

Juriy "kangax" Zaytsev wpadł na pomysł wykorzystania funkcji Object.prototype.toString. Gdy jako this przekażemy tej funkcji obiekt tablicowy, dostaniemy w wyniku wartość tekstową, która pozwala zidentyfikować tablice.

Kod: Zaznacz cały
console.log(Object.prototype.toString.call([])); // “[object Array]”

Metoda ta nie jest ograniczona wyłącznie do tablic, można przekazywać obiekty dowolnego typu.

Kod: Zaznacz cały
console.log(Object.prototype.toString.call(function () {}); // "[object Function]"

Szczególna cecha typeof

Kończąc już rozdział sprawdzania typu danych, warto wspomnieć o pewnej szczególnej właściwości operatora typeof. Jest to jedyny element języka JavaScript, który nie rzuci błędem, gdy jako parametr przekażemy mu nieistniejącą nazwę (wewnętrznie w specyfikacji mówimy o referencji).

Kod: Zaznacz cały
typeof nieistniejacazmienna; // "undefined"

Dla takiego przypadku operator również zwróci informację o typie niezdefiniowanym.

Gdybyśmy spróbowali odczytać wartość niezadeklarowanej zmiennej, kod wyrzuci ReferenceError.

Kod: Zaznacz cały
console.log(nieistniejacazmienna); // ReferenceError: nieistniejacazmienna is not defined

Konwersja typów

Kolejną typową operacją wykonywaną na danych jest konwersja z jednego typu do innego. Przykładowo, dane pochodzące z formularzy będą w przeważającej ilości tekstami, więc dane niezbędne do kalkulacji będą musiały zostać skonwertowane do typu liczbowego.

Konwersja do typu liczbowego

Skoro we wstępie wspomnieliśmy o kalkulacjach, zaczniemy od konwersji do typu liczbowego. Podstawowym sposobem jest użycie funkcji-konstruktora Number.

Kod: Zaznacz cały
console.log(
    Number(false), // 0
    Number(true), // 1
    Number("foo"), // NaN
    Number("3.14"), // 3.14
    Number("-100"), // -100
    Number("+100"), // 100
    Number("1e2"), // 100
    Number("2E3"), // 2000
    Number("0xFF"), // 255
    Number("100px"), // NaN
    Number(null), // 0
    Number(undefined), // NaN
    Number({}) // NaN
);

Jak widać, wszelkie wartości, które mają jakiś sens liczbowy, zostaną skonwertowane do liczby, dla pozostałych zostanie zwrócone NaN.

Wartości tekstowe, jeśli mają odpowiedni format liczbowy, zostaną poprawnie skonwertowane. Jeśli tylko część wartości jest liczbą (np. wymienione wyżej "3.14 foo"), to konwersja się nie powiedzie.

Warto jeszcze wymienić kilka przypadków dość specyficznych, które także zostaną skonwertowane do liczby.

JS Bin

Jak widać, białe znaki dookoła poprawnego zapisu liczb są kompletnie ignorowane, podany tekst bez problemu zostanie skonwertowany do liczby. Ponadto, pusty tekst i tekst składający się wyłącznie z białych znaków zostanie skonwertowany do liczby zero. No i na koniec najciekawsze zachowanie, teksty "Infinity" z czy bez znaku zostaną skonwertowane do odpowiedniej wartości liczbowej Infinity.

Na koniec wrócę jeszcze krótko do przypadku Number("100px"). Jest to wartość szczególna, ponieważ odczytując wartości stylów (CSS) przypisane do elementów, uzyskamy wartość właśnie takiego formatu. Dobrze by było odczytać część liczbową. Do tego możemy użyć wbudowanych funkcji parseInt i parseFloat.

Kod: Zaznacz cały
var width = parseFloat("100px"); // 100
var height = parseInt("50px", 10); // 50

Obydwie funkcje parsują teksty tak długo aż napotkają symbol niezgodny z formatem liczb. parseInt odczytuje wartości całkowite (proponuję zawsze podać system liczb, którego funkcja powinna spodziewać się w parametrze wejściowym), zaś parseFloat rozpozna liczby całkowite, ułamkowe oraz wartości Infinity.

Konwersję na typ liczbowy można wymusić na kilka sposobów, nie tylko przez funkcję Number. W zasadzie możemy wykorzystać dowolny operator, który w wyniku swojego działania zwraca liczby. Najkrótszy w zapisie wypada jednoargumentowy operator +.

JS Bin

Konwersja do typu logicznego

Tutaj też nie będzie niespodzianki, JavaScript oferuje nam funkcję-konstruktor o nazwie Boolean.

Kod: Zaznacz cały
console.log(
    Boolean(0), // false
    Boolean(1), // true
    Boolean(3.14), // true
    Boolean(NaN), // false
    Boolean(""), // false
    Boolean(" "), // true
    Boolean("foo"), // true
    Boolean({}), // true
    Boolean([]), // true
    Boolean(null), // false
    Boolean(undefined) // false
);

W pięciu przypadkach (zero, NaN, pusty string, null oraz undefined) otrzymaliśmy wartość false, dlatego te wartości plus false JavaScriptowcy nazywają falsy values.

Alternatywnie do funkcji Boolean można wykorzystać jakiś operator, który zwraca wartość logiczną. Najkrótszy w zapisie wypada operator negacji logicznej, którego trzeba zastosować podwójnie.

Kod: Zaznacz cały
console.log(
    !!0,
    !!1
);

Konwersja do typu tekstowego

Standardowo już, możemy skorzystać z funkcji-konstruktora – String.

Kod: Zaznacz cały
console.log(
    String(false), // "false"
    String(true), // "true"
    String(0), // "0"
    String(1), // "1"
    String(-2), // "-2"
    String(3.14), // "3.14"
    String(Number.MAX_VALUE), // "1.7976931348623157e+308"
    String(NaN), // "NaN"
    String({}), // "[object Object]"
    String(null),
    String(undefined)
);

Interesującym przypadkiem są tablice, które listują swoje wartości odzielone przecinkiem.

Kod: Zaznacz cały
console.log(String([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); // "0, 1, 2, 3, 4, 5, 6, 7, 8, 9"

Podobnie jest z niektórymi obiektami, np. wyrażeniami regularnymi.

Kod: Zaznacz cały
console.log(
    String(new RegExp("^d+.d+$")), // "/^d+.d+$/"
    String(new Date()) // "Wed Oct 22 2014 15:26:00 GMT+0200 (CEST)"
);

Alternatywnym sposobem jest użycie metody toString. Trzeba jednak uważać na wartości null oraz undefined, które tej metody nie posiadają.

Kod: Zaznacz cały
[false, true, 0, 1, 3.14, NaN, {}, []].forEach(function (value) {
    console.log(value.toString());
});

undefined.toString(); // TypeError: undefined has no properties
null.toString(); // TypeError: null has no properties

Najkrótszym w zapisie sposobem jest użycie operatora łączenia tekstów, np.

Kod: Zaznacz cały
console.log(
    3.14 + ""
);

Konwersja na typ obiektowy

Większość wbudowanych typów posiada swoje funkcje-konstruktory, których użyliśmy już wyżej. Funkcje te wykonują zadanie konwersji typów, gdy są wywołane w „tradycyjny” sposób. Gdy jednak wywołamy tę samą funkcję z operatorem new, wynikiem będzie obiekt danego typu, którego wewnętrzna wartość będzie odpowiadała wynikowi konwersji.

Kod: Zaznacz cały
console.log(
    typeof Number(3.14), // "number"
    typeof new Number(3.14) // "object"
);

Jeśli chcielibyśmy dla danej typu prostego uzyskać wartość obiektową, nie trzeba manualnie wywoływać konstruktora Number, String czy Boolean, tylko można użyć uniwersalnej funkcji Object, która zwróci obiekt odpowiedniego typu. Trzeba uważać jedynie na wartości null oraz undefined, dla których zwrócony zostanie nowy, pusty obiekt new Object().

Kod: Zaznacz cały
console.log(
    Object(true) instanceof Boolean, // true
    Object(3.14) instanceof Number, // true
    Object("foo") instanceof String, // true
);

Masz pytania lub wątpliwości? Odwiedź nasze forum dyskusyjne.

Rafał Kukawski

Programista, webmaster. Szczególnie upodobał sobie JavaScript i technologie klienckie, choć strona serwera i bazy danych nie stanowią tajemnicy. Tworzy też aplikacje na urządzenia mobilne. kukawski.pl.


Komentarze


HTML CSS JavaScript PHP bazy danych MySQL Flash grafika framework hosting domeny pozycjonowanie wordpress Facebook