Что получится в результате передачи объекта как аргумента в функцию и выполнения кода?

Есть блок кода:

var obj = {};

function func(x) {
  x = 1;

  return x;
}

console.log(func(obj)); // Первый
console.log(obj); // Второй

Какие результаты будут в консоле?

Теория по задаче

Разберём эту задачу с фундаментальной точки зрения. Это классический вопрос на понимание модели выполнения JavaScript, передачи аргументов и различия типов данных.

Фундамент: Типы данных в JavaScript

JavaScript имеет две категории типов данных, которые ведут себя принципиально по-разному:

Примитивные типы (Primitive Types):

  • undefined, null, boolean, number, string, symbol, bigint
  • Хранятся по значению (by value)
  • Неизменяемы (immutable) - при "изменении" создаётся новое значение
  • Сравниваются по значению: 5 === 5true

Объектные типы (Object Types)

  • Object, Array, Function, Date, RegExp и др.
  • Хранятся по ссылке (by reference)
  • Изменяемы (mutable) - можно менять содержимое
  • Сравниваются по ссылке: {} === {}false

Ключевая концепция: передача аргументов в функции

JavaScript всегда передаёт аргументы ПО ЗНАЧЕНИЮ (pass-by-value) НО: Для объектов это значение ссылки.

Аналогия:

Представь, что у тебя есть:

  • Дом (объект) по адресу 0x123
  • Визитная карточка (переменная) с этим адресом
let house = { color: "blue" }; // Визитка с адресом 0x123

Кейс 1: Передача примитива (pass-by-value):

function changePrimitive(a) {
  a = 10; // Создаётся НОВОЕ значение 10
  // Старое значение (5) не меняется
}

let num = 5;
changePrimitive(num);
console.log(num); // 5 - оригинал не изменился

Что происходит:

  • Создаётся копия значения 5
  • Копия передаётся в параметр a
  • a = 10 меняет локальную копию
  • Оригинальная переменная num не затронута

Кейс 2: Передача объекта (pass-by-value-of-reference):

function changeObject(obj) {
  obj.color = "red"; // Меняем содержимое ДОМА по адресу
}

let myHouse = { color: "blue" }; // Визитка: адрес 0x123
changeObject(myHouse);
console.log(myHouse.color); // "red" - дом изменился!

Что происходит:

  • Создаётся копия ссылки (адреса 0x123)
  • Копия ссылки передаётся в параметр obj
  • obj.color = "red" идёт по адресу и меняет сам дом
  • Оригинальная myHouse всё ещё указывает на тот же изменённый дом

Кейс 3: Переприсваивание ссылки (текущая задача):

function reassignReference(obj) {
  obj = { color: "green" }; // Создаём НОВЫЙ дом, меняем адрес на визитке
}

let house = { color: "blue" }; // Визитка: адрес 0x123
reassignReference(house);
console.log(house.color); // "blue" - старый дом не изменился!

Что происходит:

  • Параметр obj получает копию ссылки на адрес 0x123
  • obj = { color: "green" }:
    • Создаётся новый объект (новый дом по адресу 0x456)
    • Локальная переменная obj теперь хранит адрес 0x456
  • Оригинальная house продолжает хранить старый адрес 0x123

Пошаговый разбор текущей задачи

var obj = {}; // Шаг 1: Создаётся объект, obj хранит ссылку на него

function func(x) { // Шаг 3: x получает КОПИЮ ссылки от obj
  x = 1;          // Шаг 4: x теперь хранит примитив 1, связь с объектом разорвана
  
  return x;       // Шаг 5: возвращается 1
}

console.log(func(obj)); // Шаг 2 и 6: вызов функции -> 1
console.log(obj);       // Шаг 7: исходный объект остался {} 

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

До вызова функции:

[Глобальная область видимости]
obj: ref1 → { } (Объект #1 в куче)

В момент вызова func(obj):

[Глобальная область видимости]
obj: ref1 → { } (Объект #1)

[Область видимости func]
x: ref1 → { } (КОПИЯ ссылки на тот же объект)

После x = 1:

[Глобальная область видимости]
obj: ref1 → { } (Объект #1) ← НЕ ИЗМЕНЁН!

[Область видимости func]
x: 1 (теперь хранит примитив, не ссылку)

Почему это так важно на практике?

Распространённая ошибка:

function clearArray(arr) {
  arr = []; // Не очистит переданный массив!
}

let items = [1, 2, 3];
clearArray(items);
console.log(items); // [1, 2, 3] - массив не очищен!

Правильный подход:

function clearArray(arr) {
  arr.length = 0; // Изменяет существующий объект
  // или arr.splice(0, arr.length);
}

Как запомнить раз и навсегда?

Простое правило:

  • Присваивание (=) переменной нового значения → меняет только эту переменную
  • Изменение свойства (obj.key = value) → меняет сам объект, если переменная на него ссылается

ES6+ особенности

Деструктуризация не меняет правила:

function update({ data }) {
  data = 42; // Меняет только локальный параметр
}

let obj = { data: 10 };
update(obj);
console.log(obj.data); // 10

Spread оператор создаёт поверхностную копию:

function update(arr) {
  arr[0] = 100; // Изменит и оригинал!
}

let numbers = [1, 2, 3];
update([...numbers]); // Передаётся копия массива, но элементы те же
console.log(numbers); // [1, 2, 3] - не изменился
// Но если бы элементы были объектами - их изменения отразились бы!

Итого

1. Два типа данных в JS

  • Примитивы (числа, строки и др.) — хранятся и передаются по значению
  • Объекты (включая массивы, функции) — хранятся и передаются по значению ссылки

2. JavaScript всегда передаёт аргументы по значению

  • Для объектов передаётся копия ссылки, а не сам объект
  • Параметр функции становится локальной переменной

3. Критическая разница в операциях

  • x = 1переприсваивание (меняет, на что ссылается переменная x)
  • x.property = 1мутация (меняет содержимое объекта, на который ссылается x)