Przejdź do treści

typeof vs instanceof - dwa sposoby sprawdzenia typu w JS

JavaScript ma dwa podstawowe operatory do sprawdzania typu i każdy odpowiada na inne pytanie. typeof zwraca jeden z 8 stringów i ma znane pułapki (null, tablice). instanceof chodzi po łańcuchu prototypów i zawodzi dla obiektów z osadzonego <iframe>. Notatka o tym, kiedy którego użyć i dlaczego do tablic istnieje osobne Array.isArray.

Klasyczne pytanie: jak sprawdzić, czy zmienna to tablica?

const arr = [1, 2, 3];

typeof arr; // "object" - nie "array"
arr instanceof Array; // true - ale nie zawsze
Array.isArray(arr); // true - dedykowana metoda dla tablic

Skąd ta rozbieżność? Bo typeof i instanceof odpowiadają na inne pytania. typeof mówi o szerokiej kategorii (prymityw vs obiekt vs funkcja). instanceof chodzi po łańcuchu prototypów. Jeśli wiesz, którego używasz i dlaczego, większość tzw. „pułapek typowania” przestaje istnieć.

#typeof - osiem możliwych wyników

typeof x to operator zwracający string opisujący typ wartości. Specyfikacja definiuje dokładnie 8 możliwych zwrotów:

WartośćWynik
undefined"undefined"
true, false"boolean"
42, NaN, Infinity"number"
42n"bigint"
"hello""string"
Symbol()"symbol"
funkcja (() => {})"function"
wszystko inne (obiekty)"object"

Trzy pułapki, o których warto pamiętać:

typeof null; // "object" (!) - historyczny bug, nigdy nie naprawiony
typeof []; // "object" - tablica to specjalny rodzaj obiektu
typeof NaN; // "number" - NaN formalnie należy do typu Number

typeof null === "object" zostało takie od pierwszej wersji JavaScriptu i nie zostanie naprawione ze względu na wsteczną kompatybilność. Jeśli chcesz odróżnić null od innego obiektu, sprawdzaj x === null osobno. Do tablic używaj Array.isArray(x), do NaN - Number.isNaN(x) (zob. notatkę o number quirks).

typeof nie rzuca błędu nawet dla niezdefiniowanych zmiennych - zwraca "undefined". To jedyny operator w JS przyjmujący argument, który nie istnieje. Wyjątek: dla let/const użytych przed deklaracją (tzw. temporal dead zone) typeof rzuca ReferenceError.

#instanceof - sprawdza łańcuch prototypów

x instanceof Konstruktor zwraca true, jeśli Konstruktor.prototype znajduje się gdziekolwiek w łańcuchu prototypów x. Silnik chodzi po łańcuchu w górę i sprawdza każdy poziom (zob. notatkę o prototype chain):

class Animal {}
class Dog extends Animal {}

const rex = new Dog();
rex instanceof Dog; // true (Dog.prototype jest bezpośrednio)
rex instanceof Animal; // true (Animal.prototype jest wyżej w łańcuchu)
rex instanceof Object; // true (Object.prototype na końcu łańcucha)

W praktyce po instanceof sięgasz głównie do instancji własnych klas - dziedziczenie sprawdza się czysto.

Pod spodem składnia korzysta z Symbol.hasInstance - klasa może przeciążyć ten mechanizm i zwrócić co chce. W codziennym kodzie raczej go nie ruszasz, ale dobrze wiedzieć, że domyślny algorytm chodzenia po prototypach to tylko zachowanie domyślne, nie twardy wymóg języka.

Najważniejsza pułapka: instanceof nie działa dobrze, gdy obiekt pochodzi z innej strony niż ta, na której go sprawdzasz - np. ze strony osadzonej przez tag <iframe>. Każdy <iframe> ma własną, niezależną kopię Array, Date itd., więc tablica z iframe’a ma inny Array.prototype niż tablica z głównej strony - i instanceof Array zwraca false:

const arrFromFrame = window.frames[0].Array.from([1, 2, 3]);

arrFromFrame instanceof Array; // false (!) - tablica z iframe ma inny Array
Array.isArray(arrFromFrame); // true - działa zawsze

Dlatego do sprawdzania tablic używaj Array.isArray, nie instanceof Array. Array.isArray jest zdefiniowane tak, że działa niezależnie od tego, skąd pochodzi tablica.

Druga rzecz: instanceof zwraca false dla prymitywów ("abc" instanceof String to false), bo prymitywy nie mają łańcucha prototypów. Do prymitywów jest typeof.

#Kiedy którego użyć

W praktyce wszystko sprowadza się do dwóch reguł:

  1. Sprawdzasz prymityw (string, number, boolean, undefined, symbol, bigint) → typeof.
  2. Sprawdzasz konkretny rodzaj obiektu → dedykowana metoda jeśli istnieje (Array.isArray, Number.isNaN, Number.isInteger), inaczej instanceof.

Plus dwa wyjątki:

  • null sprawdzaj osobno przez x === null (bo typeof null to "object").
  • Gdy obiekt może pochodzić z osadzonego <iframe>, użyj dedykowanej metody albo Object.prototype.toString.call(x) - zwraca string typu "[object Array]" i działa wtedy, gdy instanceof zawodzi.

#Co z tego zapamiętać

  1. typeof zwraca jeden z 8 stringów - 6 prymitywów ("undefined", "boolean", "number", "bigint", "string", "symbol"), "function" dla funkcji, plus "object" dla wszystkiego innego. Pamiętaj o typeof null === "object" i typeof [] === "object".
  2. instanceof chodzi po łańcuchu prototypów. Nie działa dla prymitywów ani dla obiektów pochodzących z osadzonego <iframe>. Dla tablic używaj Array.isArray, nie instanceof Array.

W codziennym kodzie rzadko będziesz potrzebować innego niż typeof i dedykowanych metod (Array.isArray, Number.isNaN). Ale gdy sprawdzenie typu da nieoczekiwany wynik, masz teraz mapę: każdy z dwóch operatorów odpowiada na inne pytanie.

← Wróć do notatek