Język programowania JavaScript jest dynamicznie typowany, co oznacza, że zmienne mogą zmieniać typy danych podczas wykonywania programu. Mając to na uwadze, jak możemy określić typ danego obiektu w dowolnym momencie?
Tu z pomocą przychodzi operator typeof
.
Dzięki temu artykułowi dowiesz się:
- Jak sprawdzić typ zmiennej w JavaScript?
- Jak sprawdzić, czy zmienna jest tablicą?
Typowanie w języku JavaScript
Wbrew niektórym opiniom, w JavaScript istnieją typy. Sprawdzanie ich działa w runtime.
Więcej o samym “runtime” w artykule na tym blogu pt. “Co to jest runtime?”.
Zatem, czy istnieje mechanizm, który sprawdzi nam typy w fazie kompilacji?
Oczywiście, ale nie w JavaScript. Tak działa TypeScript a dokładnie TypeScript Compiler (tsc
), który to jest uruchomiony do tego, aby:
- w edytorach, aby podświetlać błędy w kodzie,
- w terminalu, aby wygenerować plik(i) JavaScript, który(e) zostaną zinterpretowane przez przeglądarkę.
typeof
w specyfikacji ECMA-262 edycja 14 (2023)
Formalny zapis prosto z oficjalnego dokumentu:
Zasada działania (bez języka formalnego, tak zwyczajnie, po polsku)
Specyfikacja jest pisana… specyficznym językiem. Myślę, że dobrze jest potrafić ją czytać.
Swego czasu blog V8 opublikował całą serię dotyczącą czytania specyfikacji ES https://v8.dev/blog/tags/understanding-ecmascript
Natomiast, gdy ktoś nigdy nie widział powyższego zapisu to opiszę jak działa
operator typeof
potocznym językiem.
Przykład: typeof val
- Zmienna
val
przechowuje wynik wykonania wyrażania znajdującego się po prawej stronie operatoratypeof
- Sprawdzamy, czy
val
należy do tzw.Reference Record
, tj.- jeśli nie należy, to zwracamy
undefined
- jeśli nie należy, to zwracamy
- Pobieramy wartość zmiennej, bo np.
val
nie musi być zmienną, może być dowolnym wyrażeniem (np. odwołaniem do właściwości obiektu -object.prop
, w takim przypadku wyliczamy jego wartość) - Jeśli
val
toundefined
zwracamy"undefined"
- Jeśli
val
tonull
zwracamy"object"
- Jeśli
val
toString
zwracamy"string"
- Jeśli
val
toSymbol
zwracamy"symbol"
- Jeśli
val
toBoolean
zwracamy"boolean"
- Jeśli
val
toNumber
zwracamy"number"
- Jeśli
val
toBigInt
zwracamy"bigint"
- Sprawdzamy, czy
val
jest obiektem - ⚠️ UWAGA: Jeśli typem
val
jestObject
orazval
jest równedocument.all
to zwracamyundefined
- Jeśli
val
jest możliwa do uruchomienia zwracamy"function"
- Zwracamy
"object"
dla wszystkich innych wartości
Reference Record
Czytając specyfikację, można się dowiedzieć, że jest to wyrażenie:
x
- pobranie referencjix[y]
- dostęp do elementu tablicy na podstawie indeksux.a
- dostęp do właściwości obiektu na podstawie nazwysuper.a
- dostęp do właściwości przodka (używamy w klasach dziedziczących)
document.all
Za czasów świetności Internet Explorera (IE), który był liderem wśród przeglądarek internetowych we wczesnych latach dwutysięcznych, próbował rozwijać język samodzielnie dodając coś nowego do JavaScript (a nawet JScript).
Jednym z takim wyjątkowych elementów, jest właśnie kolekcja document.all
.
Która to NIESTETY musi być wspierana z uwagi na kompatybilność wsteczną,
natomiast ta kolekcja jest bardzo specyficzna.
UWAGA:
typeof document.all === undefined // true
Boolean(document.all) === false // true
Pomimo, że:
document.all.toString() === '[object HTMLAllCollection]' // true
typeof document.all.length === 'number' // true
Dlatego też, w dzisiejszych czasach nie korzysta się z document.all
, a jeśli
istnieje potrzeba pobrania wszystkich elementów drzewa DOM, to można osiągnąć
to za pomocą document.querySelectorAll('*')
.
Reserved Word
Operator typeof
należy do kolekcji Reserved Words
oznacza to, że nie można użyć tej nazwy do własnych identyfikatorów,
tj. funkcji, zmiennych, itd.
let typeof = 2; // Uncaught SyntaxError: Unexpected token 'typeof'
Jest różnica między “słowami kluczowymi” a “słowami zastrzeżonymi”.
Dobrym przykładem jest wyrazasync
, który należy do słów kluczowych, ale nie do zastrzeżonych, tj. możemy go użyć jako identyfikatora zmiennej.const async = 'yes'; console.log(async); // 'yes'
Więcej informacji na ten temat w specyfikacji:
https://tc39.es/ecma262/#sec-keywords-and-reserved-words
Przypadki użycia typeof
- Q: A do czego możemy wykorzystać ten operator?
- A: Do sprawdzenia, z jakim typem wartości mamy do czynienia.
Poniżej przykłady wszystkich sprawdzeń.
// undefined
typeof undefined === 'undefined'
// ⚠️ UWAGA: Ukryty undefined
typeof document.all === 'undefined'
// boolean
typeof true === 'boolean'
typeof false === 'boolean'
typeof Boolean() === 'boolean'
// number
typeof 1 === 'number'
typeof 0 === 'number'
typeof -1 === 'number'
typeof NaN === 'number'
typeof Number() === 'number'
// string
typeof "" === 'string'
typeof "text" === 'string'
typeof String() === 'string'
typeof Date() === 'string'
// function
typeof function () {} === "function"
typeof (() => {}) === "function"
typeof Function() === "function"
typeof class {} === "function"
typeof Date === "function"
typeof Object === "function"
// object
typeof {} === 'object'
typeof Object() === 'object'
// Array object
typeof [] === 'object'
typeof Array() === 'object'
// Promise object
typeof Promise.resolve() === 'object'
typeof Promise.reject() === 'object'
// RegExp object
typeof /e/i === 'object'
typeof new RegExp('e', 'i') === 'object'
// operator `new` zawsze tworzy object
typeof new Array() === 'object'
typeof new Boolean() === 'object'
typeof new Date() === 'object'
typeof new Object() === 'object'
typeof new Function() === 'object'
typeof new String() === 'object'
// kolekcja danych
typeof new Map() === 'object'
typeof new WeakMap() === 'object'
typeof new Set() === 'object'
typeof new WeakSet() === 'object'
// ⚠️ UWAGA: Ukryty obiekt
typeof null === 'object'
// symbol
typeof Symbol() === 'symbol'
typeof Symbol('text') === 'symbol'
typeof Symbol(111) === 'symbol'
typeof Symbol.for() === 'symbol'
typeof Symbol.for('text') === 'symbol'
// bigint
typeof BigInt(10e100) === 'bigint'
Wskazówka
Każdy operator w JavaScript możemy uruchomić z wartością otoczoną w nawiasy lub też bez nich. Zatem równoważny jest zapis:
typeof x === typeof (x)
Dobrą praktyką (a raczej standardem formatowania) jest nieużywanie nawiasów.
Co z tablicą?
Tablica jest obiektem. Operator typeof
, niestety nie jest tutaj pomocny.
Aby sprawdzić, czy mamy do czynienia z tablicą, należy wykorzystać funkcję Array.isArray()
.
Array.isArray([]) === true
Array.isArray({}) === false
typeof [] === 'object'
typeof {} === 'object'
A teraz zobaczmy jak wygląda specyfikacja funkcji Array.isArray()
:
typeof
w popularnych bibliotekach
Przykład wykorzystania operatora typeof
w bibliotekach (oraz frameworkach):
Podsumowanie
Operator typeof
określa typ wartości, jaką przechowuje zmienna.
Bardzo przydatne rozwiązanie w trakcie weryfikacji, z jakimi danymi mamy do czynienia.