Как присваиваются массивы и ответ на мозголомку

2022-Aug-02

Недавно опубликовал очередную мозголомку в телеграм канале. Задача была найдена в интернете и показалась очень простой, однако вызвала бурные обсуждения. Давайте разбираться. Внимание, сама мозголомка:

Мозголомка по JavaScript

Вопрос: что будет в консоли?

Разбираемся. В JavaScript данные в переменных и константах хранятся двумя способами:

  • как примитивные значения
  • как ссылки.

Рассмотрим примитивы. Например:

let d = 8;

Что происходит под капотом JavaScript при присвоении? В памяти создается ячейка, ей присваивается имя d. В ячейку памяти записывается число 8. Теперь, когда мы обращаемся к переменной d мы однозначно и обязательно обращаемся к созданной ячейке памяти.

При создании переменной создается ячейка со значением переменной

Выполним операцию присвоения:

let f = d; 

Что произойдет?

В памяти будет создана еще одна ячейка, ей будет присвоено имя f, а в саму ячейку будет скопировано значение из ячейки d. После выполнения операции f = d, данные ячеек будут независимы и обособлены. Если мы что-то делаем с переменной d то это не отображается на f и наоборот. Проверим.

В памяти создается новая ячейка со значением
let d = 8;
let f = d;
d = 5;
console.log(d, f); // 5, 8 

таким образом, изменение переменной d не влияет на переменную f.

Когда мы говорим об объектах и массивах, то логика создания и присвоения другая. Давайте рассмотрим что происходит когда мы создаем такой объект:

Предупреждение! Автор текста вольно относится к понятиям объект и массив, хотя прекрасно понимает разницу между ними.

let j = [33,44,55];

В памяти JavaScript создается ячейку куда записывается массив [33,44,55], после чего создается еще одна ячейка и ей присваивается имя j. В ячейку j кладется ссылка на ячейку с массивом.

В ячейку памяти кладется ссылка на объект JavaScript

Т.е. при создании объекта, создается сам объект в памяти, а в переменную – кладется только ссылка на него. Разница с примитивами становится заметна после такого примера:

let j = [33,44,55];
let k = j;

Что произойдет?

В переменную k попадает не объект, а ссылка на объект из переменной j. Т.е. ссылки из k и j ведут на один и тот же объект. И если мы что-либо проделаем с переменной k то это отразится на исходном объекте, а следовательно и на переменной j.

При присвоении создается переменная со ссылкой на исходный объект

Пробуем:

let j = [33,44,55];
let k = j;
k[0] = 1000;
console.log(k); // [1000, 44, 55];
console.log(j); // [1000, 44, 55];

Надеюсь, логику вы поняли.

Теперь перейдем к мозголомке.

let user1 = {name : "John"}; // тут мы создаем объект.

В переменную user1 попадает ссылка на ячейку памяти с объектом {name : "John"}.

let user2 = user1;

В переменную user2 попадает ссылка на объект. Теперь user1 и user2 указывают на один и тот же объект.

user2.name = "Palash";

Что тут? В исходном объекте меняем свойство name.

console.log(user1); // {name : "Palash"};

Поскольку обе переменные ссылаются на один и тот же объект – увидим name : "Palash".

Итак, ссылочные типы – внутри переменной содержат только ссылку на ячейку с данными. И проводя операции присвоения, мы не создаем копию данных, мы создаем копию ссылки на исходные данные.

Еще один не очевидный вывод из сохранения ссылок в переменной. Когда мы создаем:

const b = 9;

то попытка присвоить новое значение для константы b приводит к ошибке.

А вот такой код:

const j = [33,44,55];
j[0] = 1000;

Ошибки не вызывает, поскольку в переменной j лежит ссылка и она не изменяется.

Статьи

Улучшаем input для админ-панелей на JavaScript
Подсвечиваем НЕ ЛАТИНСКИЕ СИМВОЛы в коде и тексте
Тотальное руководство по элементу Select в JavaScript