Початківець
+25 XP

👋 Починай вчитися JavaScript прямо зараз — безкоштовно!

📖

Тип object — нюанси і пастки

Чому null — object, масиви — object, і як не зламати код при копіюванні

🎒

Object — рюкзак з підписаними кишенями

Примітив (число, рядок) — це одна проста річ: монета, ключ. Object — це рюкзак де є кишені з назвами. Кишеня 'name' → 'Іван', кишеня 'age' → 25. Всередині можуть бути інші рюкзаки. Це і є головна особливість — object зберігає СТРУКТУРОВАНІ дані з іменованими полями.

Що таке object насправді

Object — це колекція пар ключ: значення. Ключі — це рядки (або Symbol), значення — будь-який тип.

js
const user = {
  name: "Іван",       // ключ: 'name', значення: рядок
  age: 25,            // ключ: 'age', значення: число
  isStudent: true,    // ключ: 'isStudent', значення: boolean
  address: {          // ключ: 'address', значення: інший object!
    city: "Київ",
    zip: "01001",
  },
};

// Доступ до властивостей
console.log(user.name);          // "Іван" — крапкова нотація
console.log(user["name"]);       // "Іван" — дужкова нотація
console.log(user.address.city);  // "Київ" — вкладений доступ

Нюанс №1: typeof null === "object"

Це одна з найвідоміших помилок (баг) в JavaScript:

js
console.log(typeof null);    // "object" ← ПОМИЛКА В САМОМУ JS
console.log(typeof {});      // "object" — тут все правильно
console.log(typeof []);      // "object" — масиви теж object!

typeof null === "object" — це баг від 1995 року (перший тиждень існування JavaScript). Null — не object, але виправити вже неможливо: занадто багато старого коду це використовує.

Як правильно перевірити null:

js
// НЕ ПРАВИЛЬНО:
if (typeof value === "object") { ... } // поймає і null!

// ПРАВИЛЬНО:
if (value !== null && typeof value === "object") { ... }

Нюанс №2: Масиви — теж object

js
let fruits = ["яблуко", "банан"];

console.log(typeof fruits);          // "object" — не "array"!
console.log(Array.isArray(fruits));  // true — ось правильна перевірка
console.log(Array.isArray({}));      // false

// Чому так? Масив в JS — це object зі спеціальними числовими ключами:
let arr = ["a", "b", "c"];
// Насправді це: { 0: "a", 1: "b", 2: "c", length: 3 }
console.log(arr[0]);      // "a"
console.log(arr.length);  // 3

Правило: щоб перевірити масив — завжди Array.isArray(value), не typeof.

Нюанс №3: Примітив vs Посилання (Reference)

Це НАЙВАЖЛИВІШИЙ нюанс object. Примітиви зберігають значення, object зберігає посилання на місце в пам'яті.

js
// Примітиви — копіюються за значенням
let a = 5;
let b = a;   // b отримує КОПІЮ значення 5
b = 10;
console.log(a); // 5 — а НЕ змінився!

// Object — копіюються за посиланням
let user1 = { name: "Іван" };
let user2 = user1;   // user2 вказує на ТОЙ САМИЙ об'єкт!
user2.name = "Марія";
console.log(user1.name); // "Марія" — user1 ТЕЖ змінився!

user2 = user1 не копіює об'єкт — лише передає адресу в пам'яті. Обидві змінні вказують на одне й те саме місце.

javascript
💬

{ ...original } — spread operator. Він копіює ВСІ властивості в новий об'єкт. Це 'поверхнева копія' (shallow copy).

Нюанс №4: Порівняння object за посиланням

js
// Примітиви порівнюються за ЗНАЧЕННЯМ
console.log(5 === 5);           // true — однакові значення
console.log("JS" === "JS");     // true

// Object порівнюється за ПОСИЛАННЯМ (адресою в пам'яті)
console.log({} === {});         // false — різні об'єкти!
console.log([] === []);         // false — різні масиви!

let a = { x: 1 };
let b = a;          // b вказує на той самий об'єкт
console.log(a === b); // true — та сама адреса!

{} === {}false, бо це два різні об'єкти в різних місцях пам'яті, навіть якщо вони однакові.

Як порівняти вміст? Перетвори в рядок: JSON.stringify(obj1) === JSON.stringify(obj2)

Нюанс №5: Shallow copy vs Deep copy

js
// Shallow copy (поверхнева) — spread
let user = { name: "Іван", address: { city: "Київ" } };
let copy = { ...user };

copy.name = "Марія";       // OK — примітив скопіювався
copy.address.city = "Одеса"; // НЕБЕЗПЕЧНО!

console.log(user.name);          // "Іван" — не змінився ✓
console.log(user.address.city);  // "Одеса" — ЗМІНИВСЯ! ✗
// Spread скопіював тільки перший рівень!
// address — вкладений об'єкт, він передався за посиланням.

// Deep copy (глибока) — через JSON
let deepCopy = JSON.parse(JSON.stringify(user));
deepCopy.address.city = "Харків";
console.log(user.address.city);  // "Одеса" — не змінився ✓

Spread ({...obj}) — поверхнева копія (shallow). Для вкладених об'єктів — JSON.parse(JSON.stringify(obj)) або structuredClone(obj) (сучасний метод).

Дві найпоширеніші помилки з object: 1. let copy = obj — думаєш що скопіював, але насправді обидві змінні вказують на один об'єкт. 2. typeof null === 'object' — перевіряєш тип і забуваєш виключити null. Заучи назубок: для копії — spread {...obj}, для перевірки на null — value !== null.

javascript
💬

structuredClone() — вбудована функція для глибокого копіювання. Підтримується у всіх сучасних браузерах.

Коментарі

Увійти або Почати щоб залишити коментар.