В чем ключевые различия между примитивами и объектами в JavaScript?

Ответ

Примитивы — это простые, неизменяемые (immutable) значения, хранящиеся по значению. Объекты — это сложные, изменяемые (mutable) структуры, хранящиеся по ссылке.

Список примитивов в JS: string, number, bigint, boolean, undefined, null, symbol.

Основные различия:

Характеристика Примитивы Объекты
Мутабельность Неизменяемы. Любая операция создаёт новое значение. Изменяемы. Свойства можно добавлять, изменять, удалять.
Способ хранения и присваивания По значению. При присваивании копируется само значение. По ссылке. При присваивании копируется ссылка на один и тот же объект в памяти.
Сравнение Сравниваются по значению. Сравниваются по ссылке (два разных объекта с одинаковыми свойствами не равны).
Наличие свойств/методов Нет. Но при обращении как к объекту JS временно оборачивает их в "обёртку" (String, Number). Есть. Могут содержать свойства и методы.

Пример, демонстрирующий разницу в присваивании и сравнении:

// ПРИМИТИВЫ (передача по значению)
let a = 10;
let b = a; // Создаётся КОПИЯ значения 10
b = 20;
console.log(a); // 10 (оригинал не изменился)
console.log(a === 10); // true (сравнение по значению)

// ОБЪЕКТЫ (передача по ссылке)
let obj1 = { value: 10 };
let obj2 = obj1; // Копируется ССЫЛКА на один объект в памяти
obj2.value = 20;
console.log(obj1.value); // 20 (изменился общий объект)
console.log(obj1 === obj2); // true (это одна и та же ссылка)

let obj3 = { value: 10 };
console.log(obj1 === obj3); // false (разные объекты, даже с одинаковыми данными)

Важный нюанс — "обёртки" для примитивов:

let str = "hello"; // Примитив string
console.log(str.toUpperCase()); // "HELLO"
// На момент вызова метода JS делает временный объект new String(str),
// вызывает метод, а затем удаляет его.

Практический вывод: При работе с объектами для создания настоящей независимой копии используйте методы глубокого клонирования (JSON.parse(JSON.stringify(obj)), structuredClone(), _.cloneDeep()).

Ответ 18+ 🔞

А, ну вот, опять про эти ваши примитивы и объекты, блядь. Сидишь такой, думаешь — ну что тут сложного? Ан нет, народ путается, как слепой кот в сортире, ёпта.

Смотри сюда, я тебе на пальцах, только без пальцев, а то их тоже можно за объекты принять, блядь.

Примитивы — это как вот эти, ну, базовые кирпичики, простые и тупые. Взять, положить, скопировать — и всё, блядь. Самые главные из них: string (буквы), number (циферки), bigint (циферки, но овердохуища), boolean (правда или ложь, как в жизни), undefined (хуй знает что), null (ничего, но специально) и symbol (какой-то магический знак, нахуй не нужный пока).

Объекты — это уже не кирпич, а целый дом, блядь, со всеми потрохами. Массив, функция, твой собственный { name: 'Вася' } — всё это объекты, хитрая жопа.

А теперь в чём разница, блядь?

Что сравниваем Примитивы Объекты
Можно ли поменять? Нихуя! Они как святой — неизменны. Сделал операцию — получил новое значение, старое на помойку. Да запросто! Живой, как тёща. Добавляй свойства, меняй, удаляй — он всё стерпит, манда с ушами.
Как хранятся и копируются? По значению. Скопировал a в b — получил два одинаковых, но РАЗНЫХ кирпича. По ссылке. Скопировал obj1 в obj2 — получил две бумажки с адресом одного и того же дома. Пнул дом через obj2 — в obj1 тоже дыра будет, пиздец.
Как сравнивать? По значению. Два яблока — они и в Африке яблоки, если одинаковые. По ссылке. Два одинаковых дома на разных улицах — это РАЗНЫЕ дома, блядь! Сравниваешь адреса, а не стены.
Есть ли свойства и методы? Вообще-то нет. Но JS, такой добрый дядя, иногда подкидывает им временную "обёртку", чтобы метод вызвать, а потом её нахуй стирает. Красота! Конечно есть, это их фишка! Сидят там, как пассажиры в автобусе.

Ну и пример, чтобы совсем мозг не взорвался:

// ПРИМИТИВЫ (живут своей жизнью)
let a = 10; // Кирпич с цифрой 10
let b = a;  // Сделали ТОЧНУЮ КОПИЮ кирпича. Теперь их два.
b = 20;     // Второй кирпич перекрасили в 20
console.log(a); // 10 (первый кирпич как был святой, так и остался)
console.log(a === 10); // true (сравниваем значения, а не кирпичи как таковые)

// ОБЪЕКТЫ (все друг за друга держатся)
let obj1 = { value: 10 }; // Построили домик
let obj2 = obj1;          // Дали другу такую же бумажку с адресом этого домика
obj2.value = 20;          // Друг пришёл и обосрал дверь
console.log(obj1.value); // 20 (а хозяин потом пришёл — охуел, блядь!)
console.log(obj1 === obj2); // true (бумажки-то с одним адресом!)

let obj3 = { value: 10 }; // Построили ТОЧНО ТАКОЙ ЖЕ домик, но в другом районе
console.log(obj1 === obj3); // false (адреса-то разные! Хотя внутри всё одинаково)

И ещё один фокус, блядь, с обёртками:

let str = "hello"; // Просто строка, примитив, тупой как пробка
console.log(str.toUpperCase()); // "HELLO"
// А как так? У примитива нет методов!
// А JS в этот момент делает так: "Опа, надо метод вызвать. Ща, я его в костюмчик нарядный заверну!"
// Создаётся временный объект `new String(str)`, у него есть метод, он вызывается,
// результат дали, а костюмчик — нахуй выкинули. Волшебство, ёпта!

Практический вывод, который в душу бьёт: Работаешь с объектами — не будь мудаком, как тот Герасим. Хочешь настоящую копию, чтобы её менять отдельно? Не просто присваивай, а клонируй, блядь, глубоко! JSON.parse(JSON.stringify(obj)), structuredClone() или там библиотеки всякие. А то потом будешь метаться и спрашивать: "Кто, сука, мне значение поменял?!"