← Назад к вопросам

В чем разница между примитивами и ссылочными типами?

1.0 Junior🔥 291 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между примитивами и ссылочными типами

В JavaScript существует фундаментальное различие между примитивными типами (Primitive Types) и ссылочными типами (Reference Types). Это различие критично для понимания работы памяти, присваивания и сравнения значений.

1. Примитивные типы: хранение в стеке

Примитивные типы хранят свои значения непосредственно в стеке памяти. Переменная содержит само значение, а не ссылку на него:

// Примитивные типы в JavaScript
const number = 42;        // Number
const string = "hello";   // String
const boolean = true;     // Boolean
const big = 123n;         // BigInt
const symbol = Symbol();  // Symbol
const undef = undefined;  // Undefined
const nothing = null;     // Null

// Каждая переменная содержит САМО значение
const a = 5;
const b = a;  // копируется само значение 5
const c = 5;

console.log(a === b); // true (одинаковые значения)
console.log(a === c); // true (одинаковые значения)

2. Ссылочные типы: хранение в куче

Ссылочные типы хранят свои значения в куче памяти. Переменная содержит только ссылку (адрес) на объект в памяти:

// Ссылочные типы в JavaScript
const obj = { name: "John" };        // Object
const arr = [1, 2, 3];               // Array
const func = function() {};          // Function
const date = new Date();             // Date
const regex = /pattern/;             // RegExp

// Переменная содержит ссылку (адрес) на объект
const a = { id: 1 };
const b = a;  // копируется ссылка, не сам объект

console.log(a === b);     // true (одна и та же ссылка)
console.log(a == b);      // true

// Но изменения видны в обеих переменных
a.id = 2;
console.log(b.id);        // 2 (потому что это один объект)

3. Визуализация в памяти

// ПРИМИТИВЫ (Стек)
// ────────────────
// | a = 5       |  <- значение прямо в стеке
// | b = "hello" |  <- значение прямо в стеке
// | c = true    |  <- значение прямо в стеке

// ССЫЛОЧНЫЕ ТИПЫ (Стек -> Куча)
// ──────────────────────────────
// Стек:              Куча:
// | a = ref ---> { name: "John" }
// | b = ref ---> { name: "John" }  (та же ссылка)
// | c = ref ---> { id: 1 }          (другой объект)

4. Копирование примитивов

При копировании примитивного типа создаётся независимая копия значения:

let x = 10;
let y = x;  // копируется значение 10

x = 20;     // изменяем x

console.log(x); // 20
console.log(y); // 10 (y не изменился)

// Каждая переменная имеет своё значение
const name1 = "Alice";
const name2 = name1;  // копируется строка

const name1Modified = "Alice Smith";
console.log(name2);  // "Alice" (не изменилась)

5. Копирование ссылочных типов

При копировании ссылочного типа копируется только ссылка, не сам объект:

const obj1 = { name: "John", age: 30 };
const obj2 = obj1;  // копируется ссылка

obj1.age = 31;      // изменяем через obj1

console.log(obj2.age); // 31 (видим изменение через obj2)

// Это один и тот же объект в памяти
console.log(obj1 === obj2); // true

const arr1 = [1, 2, 3];
const arr2 = arr1;  // копируется ссылка на массив

arr1.push(4);
console.log(arr2); // [1, 2, 3, 4] (видим изменение)

6. Глубокое копирование ссылочных типов

Для создания независимой копии объекта нужно глубокое копирование:

// ❌ Неправильно — поверхностная копия
const original = { id: 1, user: { name: "John" } };
const shallow = { ...original };

original.id = 2;
shallow.id = 1;     // OK, независимая копия

original.user.name = "Jane";
console.log(shallow.user.name); // "Jane" (вложенный объект — общий!)

// ✅ Правильно — глубокая копия
const deep = JSON.parse(JSON.stringify(original));
original.user.name = "Bob";
console.log(deep.user.name); // "Jane" (полная независимость)

// Или использовать структурированное клонирование
const deepClone = structuredClone(original);

7. Сравнение примитивов

Примитивы сравниваются по значению:

// Примитивы сравниваются по ЗНАЧЕНИЮ
const a = "hello";
const b = "hello";
const c = "hello";

console.log(a === b); // true (одинаковые значения)
console.log(b === c); // true (одинаковые значения)

// Числа
const x = 5;
const y = 5;
const z = 5;

console.log(x === y); // true (одно значение)

// Строки (даже если созданы разными способами)
const str1 = "test";
const str2 = "test";
const str3 = new String("test");

console.log(str1 === str2); // true
console.log(str1 === str3); // false (str3 — объект!)

8. Сравнение ссылочных типов

Ссылочные типы сравниваются по ссылке, не по содержимому:

// Ссылочные типы сравниваются по ССЫЛКЕ
const obj1 = { id: 1 };
const obj2 = { id: 1 };
const obj3 = obj1;

console.log(obj1 === obj2); // false (разные ссылки, хоть одинаковое содержимое)
console.log(obj1 === obj3); // true (одна ссылка)

// Массивы
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
const arr3 = arr1;

console.log(arr1 === arr2); // false (разные ссылки)
console.log(arr1 === arr3); // true (одна ссылка)

// Для сравнения содержимого нужна дополнительная логика
const deepEqual = JSON.stringify(obj1) === JSON.stringify(obj2);
console.log(deepEqual); // true

9. Передача в функции: примитивы

Примитивы передаются по значению (pass by value):

function modifyPrimitive(x) {
  x = 10;
}

let num = 5;
modifyPrimitive(num);

console.log(num); // 5 (не изменилась, передали копию)

function modifyString(str) {
  str = str + " modified";
}

let text = "hello";
modifyString(text);

console.log(text); // "hello" (строка не изменилась)

10. Передача в функции: ссылочные типы

Ссылочные типы передаются по ссылке (pass by reference):

function modifyObject(obj) {
  obj.name = "Modified"; // видно вне функции
}

const person = { name: "John" };
modifyObject(person);

console.log(person.name); // "Modified" (изменилась!)

// Но переназначение переменной внутри функции не влияет на внешнюю
function reassignObject(obj) {
  obj = { name: "New" };  // переназначение локальной переменной
}

const person2 = { name: "Jane" };
reassignObject(person2);

console.log(person2.name); // "Jane" (не изменилась)

11. Null и undefined

Оба относятся к примитивным типам, но имеют разное значение:

// undefined — отсутствие значения (по умолчанию)
let x;
console.log(x); // undefined

function noReturn() {}
console.log(noReturn()); // undefined

const obj = {};
console.log(obj.missing); // undefined

// null — явное отсутствие значения
const empty = null;
const result = someCondition ? value : null;

console.log(undefined === null);  // false
console.log(undefined == null);   // true (loose equality)

12. typeof для примитивов и ссылочных типов

// Примитивы
console.log(typeof 42);          // "number"
console.log(typeof "hello");     // "string"
console.log(typeof true);        // "boolean"
console.log(typeof undefined);   // "undefined"
console.log(typeof Symbol());    // "symbol"
console.log(typeof 123n);        // "bigint"

// Ссылочные типы (важно: все возвращают "object", кроме функций!)
console.log(typeof {});          // "object"
console.log(typeof []);          // "object"
console.log(typeof null);        // "object" (историческая ошибка!)
console.log(typeof new Date());  // "object"
console.log(typeof /regex/);     // "object"

// Функции — специальный ссылочный тип
console.log(typeof function(){});  // "function"

13. Практические примеры ошибок

// ❌ Ошибка 1: ожидание независимых копий объектов
const config1 = { debug: true };
const config2 = config1;  // ссылка, не копия

config1.debug = false;
console.log(config2.debug); // false (неожиданно!)

// ✅ Решение: явно скопировать
const config2 = { ...config1 };

// ❌ Ошибка 2: сравнение объектов с одинаковым содержимым
const user1 = { id: 1, name: "John" };
const user2 = { id: 1, name: "John" };

if (user1 === user2) { // false — разные ссылки!
  console.log("One user");
}

// ✅ Решение: сравнить содержимое
if (user1.id === user2.id && user1.name === user2.name) {
  console.log("Same user");
}

// ❌ Ошибка 3: изменение массива в функции
const original = [1, 2, 3];

function addItem(arr) {
  arr.push(4); // изменит оригинальный массив!
}

addItem(original);
console.log(original); // [1, 2, 3, 4] (неожиданно!)

// ✅ Решение: работать с копией
function addItem(arr) {
  return [...arr, 4]; // новый массив
}

14. Сравнительная таблица

ПараметрПримитивыСсылочные типы
ПамятьСтекКуча
ХранениеСамо значениеСсылка на объект
КопированиеКопируется значениеКопируется ссылка
СравнениеПо значению (===)По ссылке (===)
ИзменяемостьНеизменяемыИзменяемы
Примерыnumber, string, booleanobject, array, function
typeofРазличныеОбычно "object"

Заключение

Понимание различия между примитивами и ссылочными типами критично для:

  • Правильной работы с памятью
  • Предотвращения ошибок при копировании и изменении данных
  • Эффективной отладки приложений
  • Оптимизации производительности

Примитивы — это независимые значения, ссылочные типы — это объекты с общей ссылкой. Всегда помни об этом различии при работе с JavaScript!