Typ object — niuanse i pułapki
Dlaczego null jest obiektem, tablice są obiektami i jak nie zepsuć kodu przy kopiowaniu
Object — plecak z podpisanymi kieszeniami
Prymityw (liczba, string) to jedna prosta rzecz: moneta, klucz. Obiekt to plecak z nazwanymi kieszeniami. Kieszeń 'imie' → 'Jan', kieszeń 'wiek' → 25. W środku mogą być inne plecaki. To główna cecha — obiekt przechowuje USTRUKTURYZOWANE dane z nazwanymi polami.
Czym naprawdę jest obiekt
Obiekt to zbiór par klucz: wartość. Klucze to stringi (lub Symbol), wartości mogą być dowolnego typu.
const uzytkownik = {
imie: "Jan", // klucz: 'imie', wartość: string
wiek: 25, // klucz: 'wiek', wartość: number
jestStudentem: true, // klucz: 'jestStudentem', wartość: boolean
adres: { // klucz: 'adres', wartość: kolejny obiekt!
miasto: "Warszawa",
kod: "00-001",
},
};
// Dostęp do właściwości
console.log(uzytkownik.imie); // "Jan" — notacja z kropką
console.log(uzytkownik["imie"]); // "Jan" — notacja z nawiasem
console.log(uzytkownik.adres.miasto); // "Warszawa" — zagnieżdżony dostępPułapka #1: typeof null === "object"
To jeden z najsłynniejszych błędów JavaScript:
console.log(typeof null); // "object" ← BŁĄD W SAMYM JS
console.log(typeof {}); // "object" — to poprawne
console.log(typeof []); // "object" — tablice to też object!typeof null === "object" — to błąd z 1995 roku (pierwszy tydzień istnienia JavaScript). Null nie jest obiektem, ale nie można tego naprawić: zbyt dużo starego kodu na tym polega.
Jak poprawnie sprawdzać null:
// ŹLE:
if (typeof value === "object") { ... } // łapie też null!
// DOBRZE:
if (value !== null && typeof value === "object") { ... }Pułapka #2: Tablice to też object
let owoce = ["jabłko", "banan"];
console.log(typeof owoce); // "object" — nie "array"!
console.log(Array.isArray(owoce)); // true — poprawne sprawdzenie
console.log(Array.isArray({})); // false
// Dlaczego? Tablica w JS to obiekt ze specjalnymi kluczami numerycznymi:
let tab = ["a", "b", "c"];
// Właściwie: { 0: "a", 1: "b", 2: "c", length: 3 }
console.log(tab[0]); // "a"
console.log(tab.length); // 3Zasada: do sprawdzenia czy coś jest tablicą — zawsze używaj Array.isArray(value), nie typeof.
Pułapka #3: Typ prymitywny vs typ referencyjny
To NAJWAŻNIEJSZA pułapka obiektów. Prymitywy przechowują wartości, obiekty przechowują referencje do miejsca w pamięci.
// Prymitywy — kopiowane przez wartość
let a = 5;
let b = a; // b dostaje KOPIĘ wartości 5
b = 10;
console.log(a); // 5 — a NIE zmieniło się!
// Obiekty — kopiowane przez referencję
let user1 = { imie: "Jan" };
let user2 = user1; // user2 wskazuje na TEN SAM obiekt!
user2.imie = "Maria";
console.log(user1.imie); // "Maria" — user1 TEŻ się zmienił!user2 = user1 nie kopiuje obiektu — tylko przekazuje adres w pamięci. Obie zmienne wskazują na to samo miejsce.
{ ...oryginal } — operator spread. Kopiuje WSZYSTKIE właściwości do nowego obiektu. To 'płytka kopia'.
Pułapka #4: Obiekty porównywane przez referencję
// Prymitywy porównywane przez WARTOŚĆ
console.log(5 === 5); // true — te same wartości
console.log("JS" === "JS"); // true
// Obiekty porównywane przez REFERENCJĘ (adres w pamięci)
console.log({} === {}); // false — różne obiekty!
console.log([] === []); // false — różne tablice!
let a = { x: 1 };
let b = a; // b wskazuje na TEN SAM obiekt
console.log(a === b); // true — ten sam adres!{} === {} — false, bo to dwa różne obiekty w różnych miejscach pamięci, nawet jeśli wyglądają identycznie.
Jak porównać zawartość? Zamień na string: JSON.stringify(obj1) === JSON.stringify(obj2)
Pułapka #5: Płytka kopia vs głęboka kopia
// Płytka kopia — spread
let uzytkownik = { imie: "Jan", adres: { miasto: "Warszawa" } };
let kopia = { ...uzytkownik };
kopia.imie = "Maria"; // OK — prymityw był skopiowany
kopia.adres.miasto = "Kraków"; // NIEBEZPIECZNE!
console.log(uzytkownik.imie); // "Jan" — bez zmian ✓
console.log(uzytkownik.adres.miasto); // "Kraków" — ZMIENIONY! ✗
// Spread kopiuje tylko pierwszy poziom!
// Głęboka kopia — przez JSON
let kopiaGleboka = JSON.parse(JSON.stringify(uzytkownik));
kopiaGleboka.adres.miasto = "Gdańsk";
console.log(uzytkownik.adres.miasto); // "Kraków" — bez zmian ✓Spread ({...obj}) — płytka kopia. Dla zagnieżdżonych obiektów — użyj JSON.parse(JSON.stringify(obj)) lub structuredClone(obj) (nowoczesna metoda).
Dwa najczęstsze błędy z obiektami: 1. let kopia = obj — myślisz że skopiowałeś, ale obie zmienne wskazują na ten sam obiekt. 2. typeof null === 'object' — sprawdzasz typ i zapominasz wykluczyć null. Zapamiętaj: do kopiowania — spread {...obj}, do sprawdzenia null — value !== null.
structuredClone() — wbudowana funkcja do głębokiego kopiowania. Obsługiwana przez wszystkie nowoczesne przeglądarki.