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

Как работает передача ссылочного типа данных?

2.2 Middle🔥 151 комментариев
#JavaScript Core

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

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

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

Примитивы vs Объекты в памяти

В JavaScript есть два типа данных: примитивы (передаются по значению) и объекты (передаются по ссылке).

1. Примитивные типы (By Value)

// Примитивные типы: число, строка, булево, null, undefined, Symbol, BigInt
let a = 5;
let b = a;

a = 10;

console.log(a);  // 10
console.log(b);  // 5 - не изменилось!

// Почему? Когда b = a, копируется само значение 5
// a и b имеют разные значения в памяти

// В стеке памяти:
// a -> [10]
// b -> [5]

2. Объекты (By Reference)

// Объекты: {}, [], function, Date, RegExp и т.д.
let user1 = { name: 'Alice', age: 30 };
let user2 = user1;

user1.name = 'Bob';

console.log(user1.name);  // 'Bob'
console.log(user2.name);  // 'Bob' - изменилось!

// Почему? Когда user2 = user1, копируется ССЫЛКА на объект
// Оба user1 и user2 указывают на один и тот же объект в памяти

// В памяти:
// Стек:
//   user1 -> [адрес объекта]
//   user2 -> [адрес объекта]
// Куча:
//   [адрес] -> { name: 'Bob', age: 30 }

3. Проблема: неожиданные изменения

// ПРОБЛЕМА: Изменение через один reference влияет на другой
const person = { name: 'Alice' };
const copyPerson = person;

copyPerson.name = 'Bob';

console.log(person.name);  // 'Bob' - пользователь не ожидал!

// Как это путает:
function updateUser(user) {
  user.name = 'Charlie';
  return user;
}

const original = { name: 'Alice' };
const updated = updateUser(original);

console.log(original.name);  // 'Charlie' - оригинал тоже изменился!
console.log(original === updated);  // true - один и тот же объект

4. Shallow Copy (поверхностное копирование)

// Shallow copy копирует только верхний уровень
const original = {
  name: 'Alice',
  address: { city: 'NYC', zip: '10001' }
};

// Способ 1: Spread operator
const copy1 = { ...original };

// Способ 2: Object.assign
const copy2 = Object.assign({}, original);

// Способ 3: Array.from (для массивов)
const arr = [1, 2, 3];
const arrCopy = [...arr];

// ПРОБЛЕМА: Вложенные объекты всё ещё ссылаются на оригинал
copy1.name = 'Bob';
console.log(original.name);  // 'Alice' - имя не изменилось

copy1.address.city = 'LA';
console.log(original.address.city);  // 'LA' - город изменился!
// Потому что copy1.address и original.address указывают на один объект

5. Deep Copy (глубокое копирование)

// Deep copy копирует всё, включая вложенные объекты

// Способ 1: JSON (работает для простых объектов)
const original = {
  name: 'Alice',
  address: { city: 'NYC' }
};

const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.address.city = 'LA';
console.log(original.address.city);  // 'NYC' - не изменилось!

// ПРОБЛЕМА с JSON методом: теряются function, Date, Map, Set
const obj = {
  name: 'Alice',
  created: new Date(),
  greet: () => console.log('Hi')
};

const copy = JSON.parse(JSON.stringify(obj));
console.log(copy.created);  // строка, не Date!
console.log(copy.greet);    // undefined!

// Способ 2: Рекурсивное копирование
function deepCopy(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;  // Примитив или null
  }
  
  if (Array.isArray(obj)) {
    return obj.map(item => deepCopy(item));
  }
  
  const copy = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepCopy(obj[key]);
    }
  }
  return copy;
}

const original = {
  name: 'Alice',
  hobbies: ['reading', 'gaming']
};

const copy = deepCopy(original);
copy.hobbies.push('coding');

console.log(original.hobbies);  // ['reading', 'gaming']
console.log(copy.hobbies);      // ['reading', 'gaming', 'coding']

// Способ 3: structuredClone (новый стандарт)
const original = { name: 'Alice', created: new Date() };
const copy = structuredClone(original);

console.log(copy.created instanceof Date);  // true! (работает!)

6. Передача в функции

// ПРИМИТИВ: передаётся по значению
function modifyNumber(num) {
  num = 100;
}

let x = 5;
modifyNumber(x);
console.log(x);  // 5 - не изменилось

// ОБЪЕКТ: передаётся по ссылке
function modifyUser(user) {
  user.name = 'Bob';
}

const person = { name: 'Alice' };
modifyUser(person);
console.log(person.name);  // 'Bob' - изменилось!

// Но если переназначить саму переменную:
function replaceUser(user) {
  user = { name: 'Charlie' };  // Переназначение локальной переменной
}

const person = { name: 'Alice' };
replaceUser(person);
console.log(person.name);  // 'Alice' - не изменилось!
// Потому что user = {...} меняет только локальную ссылку

7. Практический пример: State в React

// ПЛОХО: Прямое изменение state (мутация)
const [user, setUser] = useState({ name: 'Alice', age: 30 });

function updateName(newName) {
  user.name = newName;  // Мутируем напрямую
  setUser(user);        // React может не заметить изменение!
}

// React использует === сравнение
// user === user (true), поэтому нет re-render

// ХОРОШО: Создаём новый объект
function updateName(newName) {
  const updatedUser = { ...user, name: newName };  // Shallow copy
  setUser(updatedUser);
}

// Или:
function updateName(newName) {
  setUser(prev => ({ ...prev, name: newName }));
}

// Для массивов:
const [items, setItems] = useState([1, 2, 3]);

// ПЛОХО:
items.push(4);
setItems(items);

// ХОРОШО:
setItems([...items, 4]);
// или
setItems(prev => [...prev, 4]);

8. Проверка типа и копирование

// Проверь тип данных
console.log(typeof 5);           // 'number' - примитив
console.log(typeof {});          // 'object' - объект
console.log(typeof []);          // 'object' - массив это объект!
console.log(typeof null);        // 'object' - null это ошибка в JS!

// Правильная проверка
function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}

console.log(isObject({}));       // true
console.log(isObject([]));       // true
console.log(isObject(5));        // false
console.log(isObject(null));     // false

// Определи нужен ли copy
const primitive = 5;
const primitiveCopy = primitive;  // Просто копируется

const obj = { id: 1 };
const objShallowCopy = { ...obj };  // Нужен copy для безопасности

Ответ: примитивы передаются по значению (копируются), объекты передаются по ссылке (указывают на один объект в памяти). Используй shallow copy ({...}) или deep copy (structuredClone) для безопасного копирования.