Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что меняется в ссылочном типе данных?
Важный вопрос о том, как работают ссылочные (reference) типы данных в JavaScript. Расскажу о различиях между primitives и reference types, и как это влияет на код.
Примитивные типы vs Ссылочные типы
Примитивные типы (значение хранится напрямую):
numberstringbooleanundefinednullSymbolBigInt
Ссылочные типы (хранится адрес в памяти):
ObjectArrayFunctionDateMap,Set- Etc.
Как это работает в памяти
Примитивные типы:
let a = 5; // Значение 5 хранится в переменной a
let b = a; // Копируется значение 5 в переменную b
a = 10; // Меняем a
console.log(a); // 10
console.log(b); // 5 (b не изменилась, это отдельная копия)
Ссылочные типы:
let obj1 = { name: 'John', age: 30 };
let obj2 = obj1; // obj2 указывает на ТОТ ЖЕ объект в памяти
obj1.age = 31; // Меняем свойство в исходном объекте
console.log(obj1.age); // 31
console.log(obj2.age); // 31 (изменилась, т.к. указывает на тот же объект!)
Что меняется при работе с объектами
1. Изменение свойств объекта
const user = { name: 'John' };
const user2 = user;
// Изменяем свойство
user2.name = 'Jane';
console.log(user.name); // 'Jane' - изменилась в обоих!
console.log(user2.name); // 'Jane'
// Но переассигнение переменной это другое
let user3 = user;
user3 = { name: 'Bob' }; // Переассигнили переменную
console.log(user.name); // 'Jane' - исходный объект не изменился
console.log(user3.name); // 'Bob' - user3 указывает на новый объект
2. Изменение массива
const arr1 = [1, 2, 3];
const arr2 = arr1; // arr2 указывает на тот же массив
arr2.push(4); // Добавляем элемент
console.log(arr1); // [1, 2, 3, 4] - изменился исходный массив!
console.log(arr2); // [1, 2, 3, 4]
// Переассигнление
let arr3 = arr1;
arr3 = [5, 6, 7]; // Новый массив
console.log(arr1); // [1, 2, 3, 4] - не изменился
console.log(arr3); // [5, 6, 7]
Глубокое и поверхностное копирование
Поверхностное копирование (Shallow Copy):
const original = {
name: 'John',
address: { city: 'NYC', zip: 10001 }
};
// Способ 1: Object.assign
const copy1 = Object.assign({}, original);
// Способ 2: Spread operator
const copy2 = { ...original };
// Способ 3: Array.slice для массивов
const arr = [1, [2, 3]];
const arrCopy = arr.slice();
// Проверка
copy1.name = 'Jane';
console.log(original.name); // 'John' - не изменилась (примитив)
copy1.address.city = 'LA';
console.log(original.address.city); // 'LA' - ИЗМЕНИЛАСЬ! (nested object)
Глубокое копирование (Deep Copy):
const original = {
name: 'John',
address: { city: 'NYC', zip: 10001 },
hobbies: ['coding', 'gaming']
};
// Способ 1: JSON (простой, но не универсальный)
const copy1 = JSON.parse(JSON.stringify(original));
// Способ 2: Рекурсивная функция
function deepCopy(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Array) {
return obj.map(item => deepCopy(item));
}
if (obj instanceof Date) {
return new Date(obj);
}
const copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
return copy;
}
const copy2 = deepCopy(original);
// Способ 3: structuredClone (новый, лучший способ)
const copy3 = structuredClone(original);
// Проверка
copy1.address.city = 'LA';
console.log(original.address.city); // 'NYC' - не изменилась!
Сравнение объектов
// Сравнение примитивов
console.log(5 === 5); // true (сравнивается значение)
console.log('hello' === 'hello'); // true
// Сравнение объектов
const obj1 = { x: 10 };
const obj2 = { x: 10 };
const obj3 = obj1;
console.log(obj1 === obj2); // false (разные объекты в памяти)
console.log(obj1 === obj3); // true (одна и та же ссылка)
// Для сравнения содержимого нужна функция
function isEqual(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
console.log(isEqual(obj1, obj2)); // true
Практические примеры из Frontend
1. React и изменение состояния
// НЕПРАВИЛЬНО - муторит на объекте, React не заметит изменение
function handleBadUpdate() {
const newState = state; // Ссылка на тот же объект
newState.count = newState.count + 1;
setState(newState); // React может не заметить!
}
// ПРАВИЛЬНО - новый объект
function handleGoodUpdate() {
const newState = { ...state }; // Новый объект
newState.count = newState.count + 1;
setState(newState); // React заметит изменение
}
// ИЛИ
function handleBestUpdate() {
setState(prev => ({ ...prev, count: prev.count + 1 }));
}
2. Массивы в React
// НЕПРАВИЛЬНО
function addItem(newItem) {
items.push(newItem); // Мутируем исходный массив
setItems(items); // Может не заметить React
}
// ПРАВИЛЬНО
function addItem(newItem) {
setItems([...items, newItem]); // Новый массив
}
// ПРАВИЛЬНО
function removeItem(index) {
setItems(items.filter((_, i) => i !== index)); // Новый массив
}
3. Кеширование
const cache = new Map();
function getUserData(userId) {
// Проверяем по ссылке (обычно неправильно)
if (cache.has(userId)) {
return cache.get(userId);
}
// Кешируем данные
const data = { name: 'John', age: 30 };
cache.set(userId, data);
return data;
}
// Если вернуть кешированные данные и мутировать их
const user = getUserData(1);
user.age = 31; // Изменим кешированные данные!
// Лучше возвращать копию
function getUserDataSafe(userId) {
if (cache.has(userId)) {
return { ...cache.get(userId) }; // Копируем
}
const data = { name: 'John', age: 30 };
cache.set(userId, data);
return { ...data };
}
Памятка
Что меняется при работе с ссылочными типами:
- Изменение свойства меняет данные для всех переменных указывающих на объект
- Переассигнление переменной НЕ меняет исходный объект
- Сравнение с === сравнивает ссылки, не содержимое
- Array.push(), obj.prop = значение - это мутация (меняется исходный объект)
- [...obj], {...obj} - создают новые объекты (не мутация)
- React, Redux требуют создания новых объектов при изменениях
Правило: Когда работаешь с ссылочными типами - думай о том, создаёшь ли ты новый объект или мутируешь существующий. В большинстве случаев нужны новые объекты.