Когда впервые сталкиваешься с объектами в JS, кажется, что это просто словари или хэш-мапы из других языков. Но на практике оказывается, что под капотом — целая вселенная с прототипами, дескрипторами и тонкостями мутаций. Давайте разбираться без учебникового пафоса.
Литералы vs конструкторы: что реально используют
Создать объект можно минимум четырьмя способами, но в продакшене чаще всего встречаются два:
// 99% случаев — литералы
const project = {
name: 'Vibe Coding App',
stack: ['Next.js', 'Tailwind'],
isActive: true
};
// Редкий legacy-кейс
const user = new Object();
user.name = 'Agent';
Литеральная нотация выигрывает по читаемости и лаконичности. Конструктор Object() почти не используют — разве что при работе с динамическими ключами в очень старом коде.
Интереснее вариант с Object.create(), который часто всплывает в вопросах на собесах:
const baseConfig = { env: 'dev' };
const serviceConfig = Object.create(baseConfig);
console.log(serviceConfig.env); // 'dev' (но это прототипное свойство!)
Здесь важно понимать, что serviceConfig не содержит собственного свойства env — оно тянется из прототипа. Это может неожиданно всплыть при попытке Object.keys() или hasOwnProperty.
Когда Object.assign() стреляет в ногу
Метод для копирования свойств выглядит безобидно:
const defaults = { theme: 'dark', logs: false };
const userPrefs = { logs: true };
const config = Object.assign({}, defaults, userPrefs);
Но подводные камни начинаются, когда копируешь вложенные объекты:
const original = { nested: { value: 1 } };
const copy = Object.assign({}, original);
copy.nested.value = 2; // Мутирует original.nested тоже!
Это происходит потому, что Object.assign() делает shallow copy. Для глубокого клонирования сегодня чаще используют spread-оператор с JSON или специальные утилиты типа Lodash.
Прототипы: наследование без классов
JS-объекты — это не классическая ООП-модель, а прототипная цепочка. На практике это означает, что даже у пустого объекта {} уже есть методы типа toString() в прототипе.
Когда создаешь объект через литерал, его прототипом становится Object.prototype. Но можно подменить:
const agent = {
__proto__: SomeBasePrototype,
codename: 'Neo'
};
Важно: __proto__ — deprecated, хотя и работает. Вместо него рекомендуют Object.getPrototypeOf() и Object.setPrototypeOf().
Дескрипторы: тонкая настройка свойств
Объекты в JS — это не просто key-value пары. Каждое свойство имеет дескриптор с метаинформацией:
const secret = {};
Object.defineProperty(secret, 'apiKey', {
value: '123-xyz',
writable: false,
enumerable: false
});
secret.apiKey = 'new'; // Тихий fail в non-strict режиме
console.log(Object.keys(secret)); // []
Этот механизм используется в:
- Геттерах/сеттерах
- Запрете расширения объектов (
Object.freeze()) - Реактивности во фреймворках (Vue 2)
Выводы для практики
- Для обычных DTO — литералы + spread для копирования
- Для иммутабельности —
Object.freeze()или библиотеки типа Immer - Для тонкого контроля — дескрипторы и прокси
- Наследование — лучше классы (синтаксический сахар над прототипами) или композиция
Объекты в JS — это одновременно и просто, и сложно. Главное — не переоценивать их схожесть с классическими ООП-моделями и помнить про прототипную магию.
Попробуй сам: Cursor — AI-редактор для разработчиков.
Источник: https://dev.to/sraghul_fe17c80d6b8c8023e/object-in-javascript-2797