Learn JavaScript

Типовые ошибки объектов и JSON

Объекты и JSON кажутся простыми, но именно здесь часто появляются ошибки, которые выглядят как “что-то не то в логике”. Ниже - самые типовые проблемы и понятные способы их избегать.

1) Отсутствующее свойство и undefined

Если свойства нет, чтение вернет undefined.

const user = { name: "Sam" };

console.log(user.email); // undefined

Проблема начинается, когда undefined используется дальше как объект или строка.

const user = { name: "Sam" };

try {
  console.log(user.email.toLowerCase());
} catch (e) {
  console.log(e.name); // "TypeError"
}

Практичный подход - проверка или безопасный доступ:

const user = { name: "Sam" };

const email = user.email?.toLowerCase();
console.log(email); // undefined

2) Путаница точка vs квадратные скобки

Если ключ лежит в переменной, точка не работает.

const user = { name: "Sam" };
const key = "name";

console.log(user.key);  // undefined
console.log(user[key]); // "Sam"

Скобки нужны также для ключей с дефисами и пробелами.

const obj = { "full name": "Sam Doe" };

console.log(obj["full name"]); // "Sam Doe"

3) Перепутан объект и массив

У массива индексы, у объекта ключи.

const users = [{ id: 1 }, { id: 2 }];

console.log(users[0].id); // 1
console.log(users.id);    // undefined
const user = { id: 1, name: "Sam" };

console.log(user.id);  // 1
console.log(user[0]);  // undefined

Если структура данных не очевидна, помогает Array.isArray.

const value = [];

console.log(Array.isArray(value)); // true

4) Мутация через общую ссылку

“Сломалось само” обычно означает, что две переменные указывают на один объект.

const a = { n: 1 };
const b = a;

b.n = 99;

console.log(a.n); // 99

Чтобы не менять исходный объект, создается копия:

const a = { n: 1 };
const b = { ...a, n: 99 };

console.log(a.n); // 1
console.log(b.n); // 99

5) Поверхностная копия и вложенные объекты

Spread копирует только верхний уровень.

const a = { user: { name: "Sam" } };
const b = { ...a };

b.user.name = "Alex";

console.log(a.user.name); // "Alex"

Если нужно изменить вложенное поле без влияния на оригинал, копируется нужная ветка:

const a = { user: { name: "Sam" } };

const b = {
  ...a,
  user: {
    ...a.user,
    name: "Alex"
  }
};

console.log(a.user.name); // "Sam"
console.log(b.user.name); // "Alex"

6) JSON.parse падает на невалидной строке

JSON.parse бросает SyntaxError, если строка не валидный JSON.

const input = '{ "a": 1, }';

try {
  const data = JSON.parse(input);
  console.log(data);
} catch (e) {
  console.log(e.name); // "SyntaxError"
}

JSON не допускает запятые в конце и комментарии.

7) В JSON только двойные кавычки

Это валидный JavaScript-объект, но не JSON:

const obj = { a: 1 };

JSON-строка выглядит так:

const json = "{\"a\": 1}";

В JSON строки и ключи должны быть в двойных кавычках.

8) JSON.stringify “теряет” данные

undefined и функции в JSON не попадают.

const obj = { a: 1, b: undefined, c: () => 1 };

console.log(JSON.stringify(obj)); // {"a":1}

NaN и Infinity превращаются в null.

const obj = { n: NaN, i: Infinity };

console.log(JSON.stringify(obj)); // {"n":null,"i":null}

9) Циклические ссылки ломают stringify

const a = {};
a.self = a;

try {
  JSON.stringify(a);
} catch (e) {
  console.log(e.name); // "TypeError"
}

Это частая причина ошибки при попытке сериализовать “большой объект состояния”.

10) JSON копирование подходит не всегда

Иногда делают:

const copy = JSON.parse(JSON.stringify(obj));

Это работает только для простых данных. Теряются Date, undefined, функции, а циклические ссылки дают ошибку.

const data = { createdAt: new Date("2025-01-01T00:00:00Z") };

const copy = JSON.parse(JSON.stringify(data));

console.log(typeof copy.createdAt); // "string"

11) Проверка “есть ли ключ” через truthy/falsy

Если значение может быть 0, false или "", проверка через if (obj.key) может дать ложный результат.

const data = { retries: 0 };

if (data.retries) {
  console.log("has retries");
} else {
  console.log("no retries");
}

Более точная проверка:

const data = { retries: 0 };

console.log("retries" in data); // true
console.log(data.retries ?? 3); // 0

Что стоит запомнить

  • Отсутствующее свойство дает undefined, а цепочка чтения может привести к TypeError.
  • Для ключей в переменной нужны квадратные скобки.
  • Объекты и массивы передаются по ссылке, случайные мутации встречаются часто.
  • Spread делает поверхностную копию - для вложенности копируется нужная ветка.
  • JSON строгий: только двойные кавычки, без комментариев и запятых в конце.
  • JSON.parse часто требует try/catch, JSON.stringify теряет часть типов и не работает с циклами.