Метод toJSON() у JavaScript
toJSON() це спеціальний метод, який JavaScript викликає автоматично під час серіалізації об'єкта через JSON.stringify(). Він дозволяє контролювати, що саме потрапить у JSON-рядок: якщо в об'єкта є toJSON(), серіалізується результат виклику цього методу, а не сам об'єкт.
Теорія
TL;DR
JSON.stringify(obj)спершу перевіряє, чи є в об'єкта методtoJSON().- Якщо є, серіалізується результат його виклику, а не сам об'єкт.
- Працює рекурсивно:
toJSON()вкладеного об'єкта теж викликається. - Можна оголошувати у власних класах, зручно для «чистого» вигляду даних.
- Повертати дозволено будь-яке значення: об'єкт, рядок, число,
null. Dateмає власнийtoJSON(), тому автоматично стає ISO-рядком.- На
JSON.parse()метод не впливає взагалі.
Швидкий приклад
const user = {
name: 'Alice',
age: 25,
password: 'secret',
toJSON() {
// обираємо, що потрапить у JSON
return { name: this.name, age: this.age };
}
};
console.log(JSON.stringify(user));
// '{"name":"Alice","age":25}'password не потрапив у JSON, бо toJSON() повертає лише name і age.
Як це працює під капотом
Коли ви викликаєте:
JSON.stringify(obj)JavaScript робить таке:
-
Перевіряє, чи є в
objметодtoJSON(). Якщо є, викликає його:javascriptconst value = obj.toJSON(); -
Серіалізує результат виклику, а не сам об'єкт.
Тобто toJSON() це точка розширення самого формату: тип сам вирішує, яким він є у JSON.
Рекурсія у вкладених об'єктах
toJSON() спрацьовує для всіх вкладених об'єктів, а не тільки для кореневого.
const user = {
name: 'Alice',
stats: {
score: 42,
toJSON() {
return 'Top Secret'; // підміняємо об'єкт рядком
}
}
};
console.log(JSON.stringify(user));
// '{"name":"Alice","stats":"Top Secret"}'Метод toJSON() усередині stats викликався автоматично і підмінив вкладений об'єкт.
toJSON() у класах і у вбудованих типах
Метод зручно оголошувати у власних класах, щоб красиво серіалізувати екземпляри:
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
toJSON() {
return {
user: this.name,
age: this.age
};
}
}
const alice = new User('Alice', 25);
console.log(JSON.stringify(alice));
// '{"user":"Alice","age":25}'Так визначають «чисте» представлення даних, наприклад для відправки на сервер.
Вбудовані типи поводяться по-різному:
| Тип | Що робить toJSON() | Приклад |
|---|---|---|
Date | Повертає ISO-рядок | new Date().toJSON() дає "2025-10-14T17:00:00.000Z" |
Map / Set | Не серіалізуються (за замовчуванням) | JSON.stringify(new Map()) дає {} |
BigInt | Не підтримується | Помилка: TypeError: Do not know how to serialize a BigInt |
Що можна повертати і як це поєднується з replacer
Повертати можна будь-яке значення: рядок, число, навіть null.
const product = {
name: 'T-shirt',
price: 1500,
toJSON() {
return `${this.name}: ${this.price} UAH`;
}
};
console.log(JSON.stringify(product));
// '"T-shirt: 1500 UAH"'Повернутий рядок стає результатом серіалізації цілком.
Якщо в об'єкта є і toJSON(), і переданий replacer, то спершу викликається toJSON(), а вже його результат потрапляє у replacer:
const obj = {
name: 'Alice',
toJSON() {
return { custom: true };
}
};
console.log(JSON.stringify(obj, (key, value) => {
if (key === 'custom') return 'ok';
return value;
}));
// '{"custom":"ok"}'Підсумок
| Питання | Відповідь |
|---|---|
| Що робить | Визначає, що саме буде серіалізовано у JSON.stringify() |
| Де викликається | Автоматично під час JSON.stringify() |
| Що повертає | Будь-яке значення (об'єкт, рядок, число тощо) |
| Чи працює рекурсивно | Так, для вкладених об'єктів |
| Чи можна в класах | Так |
Чи впливає на JSON.parse() | Ні, працює лише на серіалізацію |
Типові помилки
- Чекати, що
JSON.parse()поверне екземпляр класу.toJSON()односторонній: для зворотного перетворення потрібен власний конструктор, фабрика абоreviverуJSON.parse(). - Оголошувати
toJSONяк стрілкову функцію у полі класу. Тодіthisвказує не туди, куди ви очікуєте; потрібен звичайний метод. - Повертати з
toJSON()сам об'єкт (return this). Для кореневого виклику це нескінченна рекурсія або той самий результат без користі. - Думати, що
toJSON()шифрує чи захищає дані. Він лише формує представлення; приховане поле легко дістати напряму з об'єкта. - Забувати, що метод спрацьовує і для вкладених значень. Один
toJSON()глибоко в дереві може непомітно змінити весь payload. - Розраховувати на
toJSON()дляMap,SetчиBigInt. У них його немає (аBigIntкидаєTypeError), тому перетворення треба писати самому.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.