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

Что меняется в ссылочном типе данных?

1.7 Middle🔥 111 комментариев
#JavaScript Core

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

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

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

Что меняется в ссылочном типе данных?

Важный вопрос о том, как работают ссылочные (reference) типы данных в JavaScript. Расскажу о различиях между primitives и reference types, и как это влияет на код.

Примитивные типы vs Ссылочные типы

Примитивные типы (значение хранится напрямую):

  • number
  • string
  • boolean
  • undefined
  • null
  • Symbol
  • BigInt

Ссылочные типы (хранится адрес в памяти):

  • Object
  • Array
  • Function
  • Date
  • Map, 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 };
}

Памятка

Что меняется при работе с ссылочными типами:

  1. Изменение свойства меняет данные для всех переменных указывающих на объект
  2. Переассигнление переменной НЕ меняет исходный объект
  3. Сравнение с === сравнивает ссылки, не содержимое
  4. Array.push(), obj.prop = значение - это мутация (меняется исходный объект)
  5. [...obj], {...obj} - создают новые объекты (не мутация)
  6. React, Redux требуют создания новых объектов при изменениях

Правило: Когда работаешь с ссылочными типами - думай о том, создаёшь ли ты новый объект или мутируешь существующий. В большинстве случаев нужны новые объекты.

Что меняется в ссылочном типе данных? | PrepBro