Статьи

Совет: как работают ссылки на JavaScript

Эта статья была первоначально опубликована на Medium .

TL; DR: в JavaScript нет указателей, и ссылки работают не так, как обычно в большинстве других популярных языков программирования. В JavaScript просто НЕ возможно иметь ссылку из одной переменной в другую. И, только составные значения (например, Object или Array) могут быть назначены по ссылке.

Морфеус: Что если я скажу тебе, ты можешь писать ссылки на JavaScript?

В статье используются следующие термины:

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

Скалярные типы JavaScript являются примитивами, но некоторые языки, такие как Ruby, имеют скалярные ссылочные типы. Обратите внимание, что в JavaScript скалярные значения примитивов неизменны, а составные значения изменчивы.

Нижняя линия:

  1. Значение typeof назначенное переменной, решает, будет ли значение сохранено с назначением по значению или назначением по ссылке.
  2. При назначении переменной скалярные значения примитива (Number, String, Boolean, undefined, null, Symbol) присваиваются по значению, а составные значения присваиваются по ссылке.
  3. Ссылки в JavaScript указывают только на содержащиеся значения, а НЕ на другие переменные или ссылки.
  4. В JavaScript скалярные примитивные значения неизменны, а составные значения изменчивы.

Быстрый пример присвоения по значению:

В приведенном ниже фрагменте кода мы присваиваем скалярное примитивное значение (число) переменной и, таким образом, здесь применяется присвоение по значению. Во-первых, переменная batman инициализируется, и когда переменной superman присваивается значение, сохраненное в batman , она создает новую копию значения и сохраняет ее. Когда переменная superman модифицируется, batman остается без изменений, так как они указывают на разные значения.

 var batman = 7; var superman = batman; //assign-by-value superman++; console.log(batman); //7 console.log(superman); //8 

Пример присвоения по значению

Быстрый пример назначения по ссылке:

В приведенном ниже фрагменте кода мы присваиваем составное значение (массив) переменной и, таким образом, здесь применяется назначение по ссылке. Переменные flash и quicksilver являются ссылками на одно и то же значение (также называемое общим значением). Ссылки будут указывать на обновленное значение при изменении общего значения.

 var flash = [8,8,8]; var quicksilver = flash; //assign-by-reference quicksilver.push(0); console.log(flash); //[8,8,8,0] console.log(quicksilver); //[8,8,8,0] 

Пример назначения по ссылке

Как создать новую ссылку

Когда составное значение в переменной переназначается, создается новая ссылка. В JavaScript, в отличие от большинства других популярных языков программирования, ссылки являются указателями на значения, хранящиеся в переменных, а НЕ указателями на другие переменные или ссылки.

 var firestorm = [3,6,3]; var atom = firestorm; //assign-by-reference console.log(firestorm); //[3,6,3] console.log(atom); //[3,6,3] atom = [9,0,9]; //value is reassigned (create new reference) console.log(firestorm); //[3,6,3] console.log(atom); //[9,0,9] 

Создание новой ссылки

Как работают ссылки, когда значения передаются как параметры функции

В приведенном ниже фрагменте кода переменная magneto является составным значением (массивом), поэтому она присваивается переменной (аргумент функции) x в качестве ссылки.

Метод Array.prototype.push вызываемый внутри IIFE, изменяет значение в переменной magneto через ссылку JavaScript. Но переназначение переменной x создает новую ссылку, и дальнейшие модификации к ней НЕ влияют на ссылку на переменную magneto .

 var magneto = [8,4,8]; (function(x) { //IIFE x.push(99); console.log(x); //[8,4,8,99] x = [1,4,1]; //reassign variable (create new reference) x.push(88); console.log(x); //[1,4,1,88] })(magneto); console.log(magneto); //[8,4,8,99] 

Как изменить исходное значение в составной переменной, передаваемой как аргумент функции через ссылку JavaScript

Решением здесь будет изменение существующего составного значения, на которое указывает ссылка. В приведенном ниже фрагменте кода переменная wolverine является составным значением (массив), и при вызове IIFE переменная (аргумент функции) x назначается ссылкой.

Свойство Array.prototype.length можно использовать для создания пустого массива, установив его значение в 0 . Таким образом, переменная росомаха изменяется на новое значение, установленное в переменной x через ссылку JavaScript.

 var wolverine = [8,7,8]; (function(x) { //IIFE x.length = 0; //make empty array object x.push(1,4,7,2); console.log(x); //[1,4,7,2] })(wolverine); console.log(wolverine); //[1,4,7,2] 

Как сохранить составное значение через присвоение по значению

Решением здесь будет создание ручной копии составного значения, а затем присвоение скопированного значения переменной. Следовательно, ссылка на присвоенное значение НЕ указывает на исходное значение.

Рекомендуемый подход для создания (поверхностной) копии составного значения (объекта Array) состоит в том, чтобы вызвать для Array.prototype.slice метод Array.prototype.slice без передаваемых аргументов.

 var cisco = [7,4,7]; var zoom = cisco.slice(); //create shallow copy cisco.push(77,33); console.log(zoom); //[7,4,7] console.log(cisco); //[7,4,7,77,33] 

Diagram4

Как сохранить скалярное значение примитива через присвоение по ссылке?

Решение в этом случае заключалось бы в том, чтобы обернуть скалярное примитивное значение в составное значение (т.е. объект или массив) в качестве значения его свойства. Таким образом, он может быть назначен по ссылке. В приведенном ниже фрагменте кода скалярное значение примитива с переменной speed задается как свойство объекта flash . Следовательно, он присваивается по ссылке при вызове IIFE переменной (аргумент функции) x .

 var flash = { speed: 88 }; (function (x) { //IIFE x.speed = 55; })(flash); console.log(flash.speed); //55 

Резюме

Нео останавливая пули в воздухе. Титр: Ссылки в JavaScript

Хорошее понимание ссылок в JavaScript может помочь разработчикам избежать многих распространенных ошибок и написать лучший код.

Удачного кодирования!