Advanced JavaScript Series — Часть 6.2: Передача по значению и передача по ссылке, мелкое и глубокое копирование, коэрцитивность типов

Передача по значению

В JavaScript pass by value функция вызывается путем непосредственной передачи значения переменной в качестве аргумента. Поэтому даже изменение аргумента внутри функции не влияет на переменную, переданную извне функции.

  • Важно отметить, что в JavaScript все аргументы функций всегда передаются по значению. То есть JavaScript копирует значения передаваемых переменных в аргументы внутри функции.


Кредиты — Рейна Митчелл

Пример-

Код-

let a=5
let b=a

b++;
console.log(b)
console.log(a)
Вход в полноэкранный режим Выход из полноэкранного режима

Выход-

6
5
Ввести полноэкранный режим Выход из полноэкранного режима

Передача по ссылке —

В Pass by Reference функция вызывается путем прямой передачи ссылки/адреса переменной в качестве аргумента. Изменение аргумента внутри функции влияет на переменную, переданную извне функции.

  • В JavaScript объекты и массивы всегда передаются по ссылке.

Пример-

Код —

let obj1={
  a: 'a',
  b: 'b',
  c: 'c'
}

// shallow copying the object using the spread operator
let obj2={...obj1}
obj2.b='d'

console.log(obj2)
console.log(obj1)
Вход в полноэкранный режим Выход из полноэкранного режима

или

let obj1={
  a: 'a',
  b: 'b',
  c: 'c'
}

// another way of copying the object
let obj2=Object.assign({}, obj1)
obj2.b='d'

console.log(obj2)
console.log(obj1)
Войти в полноэкранный режим Выход из полноэкранного режима

Выход-

{
  a: "a",
  b: "d",
  c: "c"
}
{
  a: "a",
  b: "b",
  c: "c"
}
Войти в полноэкранный режим Выход из полноэкранного режима
  • Но это лишь поверхностная копия исходного объекта.

Глубокая копия означает, что все значения новой переменной скопированы и отсоединены от исходной переменной. Неглубокая копия означает, что некоторые (под-)значения все еще связаны с исходной переменной.

  • Давайте разберемся в этом с помощью примера.

Пример —

let obj1 = {
  a: 'a',
  b: 'b',
  c: {
    d: 'd'
  }
};

let obj2={...obj1}
obj2.c.d='f'

console.log(obj2)
console.log(obj1)
Вход в полноэкранный режим Выход из полноэкранного режима

Выход-

{
  a: "a",
  b: "b",
  c: {
    d: "f"
  }
}
{
  a: "a",
  b: "b",
  c: {
    d: "f"
  }
}
Войти в полноэкранный режим Выход из полноэкранного режима

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

  • Проблема с поверхностным копированием заключается в том, что если пользователь вносит изменения в сложный объект (обновляет свойство street объекта адреса) объекта-источника userName, это также отражается в объекте назначения, поскольку он указывает на тот же адрес памяти и наоборот.

  • Таким образом, мы переходим к глубокому копированию. Глубокое копирование означает, что значение новой переменной отсоединено от исходной переменной, в то время как поверхностное копирование означает, что некоторые значения все еще связаны с исходной переменной.

  • Давайте разберем глубокое копирование с помощью примера.

Пример —

Код —

let obj1 = {
  a: 'a',
  b: 'b',
  c: {
    d: 'd'
  }
};

// converts obj1 to string and then parses it into a new object
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.c.d = 'f'

console.log(obj2)
console.log(obj1)
Вход в полноэкранный режим Выход из полноэкранного режима

Выход-

{
  a: "a",
  b: "b",
  c: {
    d: "f"
  }
}
{
  a: "a",
  b: "b",
  c: {
    d: "d"
  }
}
Ввести полноэкранный режим Выход из полноэкранного режима
  • Здесь мы видим, что изменения, сделанные в obj2, не отражаются в obj1, поэтому мы можем сказать, что это глубокая копия и два объекта не связаны.

Как сравнить два объекта, расположенных в разных местах памяти, но имеющих одинаковое значение?

  • Чтобы ответить на этот вопрос, я нашел эту тему Stack Overflow, которая безупречно отвечает на этот вопрос, и я не смог объяснить это лучше, чем добавив ссылку на эту тему: Stack Overflow

  • Если ссылка по какой-то причине не открывается, то вот самый быстрый и ограниченный способ сравнения значений объектов в разных местах памяти —

JSON.stringify(obj1) === JSON.stringify(obj2)
Войдите в полноэкранный режим Выйти из полноэкранного режима


Кредиты — Mathwarehouse


Сложный вопрос для проверки знаний-

const number = 100
const string = "Jay"
let obj1 = {
  value: "a"
}
let obj2 = {
  value: "b"
}
let obj3 = obj2;

function change(number, string, obj1, obj2) {
    number = number * 10;
    string = "Pete";
    obj1 = obj2;
    obj2.value = "c";
}

change(number, string, obj1, obj2);

// guess which variables will get updated
console.log(number); 
console.log(string);
console.log(obj1.value);
Войти в полноэкранный режим Выход из полноэкранного режима

Выход —

100
Jay
a
Войти в полноэкранный режим Выход из полноэкранного режима

Принуждение типов —

Принуждение типов — это автоматическое или неявное преобразование значений из одного типа данных в другой (например, строк в числа).


Кредиты — Счет

Пример-

Код-

const value1 = '5';
const value2 = 9;
let sum = value1 + value2;

console.log(sum);
Вход в полноэкранный режим Выход из полноэкранного режима

Выход-

"59"
Ввести полноэкранный режим Выход из полноэкранного режима
  • В приведенном выше примере JavaScript преобразовал 9 из числа в строку, а затем объединил эти два значения вместе, в результате чего получилась строка 59. У JavaScript был выбор между строкой и числом, и он решил использовать строку.

  • Компилятор мог бы преобразовать 5 в число и вернуть сумму 14, но он этого не сделал. Чтобы вернуть такой результат, вам придется явно преобразовать 5 в число с помощью метода Number():

sum = Number(value1) + value2;
Вход в полноэкранный режим Выйти из полноэкранного режима

В качестве примера коэрцитивности типов на практике посмотрите сравнительную таблицу JavaScript, которая показывает, как ведет себя оператор свободного равенства == для различных типов.

Неявное и явное принуждение

  • Принуждение типов может быть явным и неявным.

  • Когда разработчик выражает намерение преобразовать тип путем написания соответствующего кода, например Number(value), это называется явным принуждением типа (или приведением типа).

  • Поскольку JavaScript является слабо типизированным языком, значения также могут преобразовываться между различными типами автоматически, и это называется неявным приведением типов.


Связь со мной —

  • GitHub
  • LinkedIn

Приложение-

  1. Advanced JavaScript Series — Часть 1: За кулисами (JavaScript Engine, ATS, скрытые классы, сборка мусора)
  2. Серия Advanced JavaScript — Часть 2: Контекст выполнения и стек вызовов
  3. Серия Advanced JavaScript — Часть 3: Странное поведение JS, строгий режим и подъем, временная мертвая зона
  4. Серия Advanced JavaScript — Часть 4.1: Глобальный, функциональный и блочный диапазоны, лексический и динамический диапазоны
  5. Серия Advanced JavaScript — часть 4.2: Цепочки диапазонов и их работа, лексическое и переменное окружение
  6. Advanced JavaScript Series — Часть 5: IIFE иamp; ключевое слово ‘this’ в JS (хитрые примеры), call(), apply(), bind(), Currying(Functional Prog)
  7. Advanced JavaScript Series — Часть 6.1: Все в JS является объектом? Странное поведение JS раскрыто, примитивные типы без примитивов
  8. Advanced JavaScript Series — часть 6.2: Передача по значению и передача по ссылке, мелкое и глубокое копирование, коэрцитивность типов
  9. Продвинутая серия JavaScript — Часть 7: Граждане первого сорта & Функции высшего порядка
  10. Серия Advanced JavaScript — Часть 8: 2 столпа ~ замыкания и наследование прототипов
  11. Серия Advanced JavaScript — Часть 9: Функции конструктора, объектно-ориентированный подход, ключевое слово new.

Ссылки-

  1. https://flexiple.com/javascript-pass-by-reference-or-value/
  2. https://developer.mozilla.org/en-US/docs/Glossary/Type_coercion
  3. https://www.freecodecamp.org/news/js-type-coercion-explained-27ba3d9a2839/

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *