Привет, разработчики! Сегодня мы обсудим одну из самых фундаментальных концепций в Javascript «Pass by Value» и «Pass by Reference». Эта концепция настолько важна, что даже этот вопрос часто всплывает на собеседовании. Почему? Понимание того, как это работает, поможет нам стать лучшим программистом, который сможет понять поток данных в приложении.
Прежде чем мы сможем погрузиться в объяснение. Мы должны знать, что в Javascript типы данных делятся на две категории: примитивные и непримитивные.
Вот список примитивных типов данных в Javascript:
-
string
— массив символов -
number
— целое число, поплавок и т.д.. -
bigint
— в случае, если вам нужны целые значения, большие, чем диапазон, поддерживаемыйNumber
-
boolean
— истина или ложь -
null
— пустое значение -
symbols
— уникальное значение или часто используется как уникальный идентификатор, который не равен никакому другому значению -
undefined
— объявленная переменная, но значение еще не присвоено.
Между тем, все объекты, массивы и функции попадают под категорию непервичных типов данных.
Хорошо, мы разобрались. Давайте перейдем к объяснению!
Передача по ссылке
Давайте посмотрим на приведенный ниже код, чтобы понять, что такое передача по ссылке.
let john = {
name: 'John Doe',
gender: 'male',
score: 95
};
let JohnDupe = john;
console.log(JohnDupe); // {name: 'John Doe', gender: 'male', score: 95}
console.log(john); // {name: 'John Doe', gender: 'male', score: 95}
Итак, мы создали новую переменную JohnDupe
и присвоили ей значение из John
. Что произойдет, если мы изменим значение переменной JohnDupe
?
johnDupe.score = 50;
console.log(JohnDupe); // {name: 'John Doe', gender: 'male', score: 50}
console.log(john); // {name: 'John Doe', gender: 'male', score: 50}
Обратите внимание, что оценка значения John
также изменилась! Что же произошло на самом деле? Когда мы создаем John
(новый объект), мы сохраняем значение в памяти по некоторому адресу. И когда мы присваиваем значение из John
в johnDupe
, фактически вместо создания копии John
, значение JohnDupe
ссылается на значение (адрес) John
в памяти.
Чтобы решить вышеописанную проблему, нам нужно фактически создать копию John
и присвоить ее новой переменной, и тогда мы сможем изменить оценку, не затрагивая оригинальную John
.
let john = {
name: 'John Doe',
gender: 'male',
score: 95
};
let anotherJohn = {...john}
anotherJohn.score = 50;
console.log(john); // {name: 'John Doe', gender: 'male', score: 95}
console.log(anotherJohn); // {name: 'John Doe', gender: 'male', score: 50}
В приведенном выше коде мы скопировали john
с помощью оператора spread, а затем присвоили его anotherJohn
. Создавая новый объект, anotherJohn
фактически хранит скопированный объект в памяти с адресом, отличным от адреса john
. anotherJohn
больше не ссылается на john
, как раньше, это совершенно разные сущности. Чтобы доказать это, посмотрите на приведенный ниже код
let person = {
name: 'Nicola Tesla',
gender: 'male',
};
let personDupe = person;
let anotherPerson = {
name: 'Nicola Tesla',
gender: 'male',
};
console.log(person === personDupe); // true
console.log(person === anotherPerson); // false
В примере выше мы попытались сравнить equal person === personDupe
, что приведет к выводу true
, так как personDupe
указывает на адрес из person
. Однако когда мы попытались сравнить равные person === anotherPerson
, будет выведено false
, потому что они не являются одним и тем же объектом, даже если у них одинаковые свойства и значения.
Такое же поведение наблюдается и с Array
.
let fruits = ['apple 🍎', 'strawberry 🍓', 'banana 🍌'];
let fruitsDupe = fruits;
let anotherFruits = ['apple 🍎', 'strawberry 🍓', 'banana 🍌'];
console.log(fruits === fruitsDupe); // true
console.log(fruits === anotherFruits); // false
У меня есть аналогия, чтобы нам было легче понять «передачу по ссылке», если представить это как google sheet / google docs.
Допустим, группе студентов преподаватель дает задание изложить свое мнение о глобальном потеплении. 🌎 Учитель хочет, чтобы задание было в формате одного файла.
Джон, как президент класса, берет на себя инициативу и создает google docs, чтобы ученики могли легко сотрудничать. Затем Джон делится ссылкой с одноклассниками. Мэри — первая ученица, которая внесла изменения в документ и добавила несколько абзацев своей идеи о глобальном потеплении. Позже вечером Сьюзи открыла google docs и добавила свое мнение, затем Энди и так далее. Наконец, после того как все ученики закончили добавлять свое мнение, Джон передал документы google doc учителю. Все хорошо, и класс получил «А» за задание ✨.
Как видно из приведенного выше примера, независимо от того, сколько раз ученики вносили изменения в google docs, в конечном итоге они изменили только один файл (главный файл).
Пока все хорошо! Мы знаем, что такое передача по ссылке. ✨
Передача по значению
Если вы уже поняли, что такое передача по ссылке, то вы уже поняли, что такое передача по значению. Примитивные значения, такие как числа или строки, фактически создают копию.
const num = 10
function passByValue(val) {
return val += 1
}
console.log('[passByValue]:', passByValue(num)) // [passByValue]: 11
console.log('[num]:', num) // [num:] 10
В приведенном выше коде мы создаем переменную num
со значением 10. Также мы создаем функцию passByValue
, которая принимает значение в качестве параметра и прибавляет его к 1. Если мы попытаемся выполнить приведенный выше код, passByValue
вернет 11, однако переменная num
по-прежнему будет равна 10. Первоначальное значение num
никак не изменится.
Надеюсь, эта небольшая статья поможет вам понять концепцию передачи по ссылке и передачи по значению. Основное различие между передачей по значению и передачей по ссылке заключается в том, что при передаче по значению создается новое место в памяти и делается копия значения. Однако при передаче по ссылке вместо создания копии создается ссылка на значение, хранящееся в памяти.