10 способов поменять местами два значения в JavaScript

1) Использование временной переменной

function swapWithTemp(num1,num2){
  console.log(num1,num2)

  var temp = num1;
  num1 = num2;
  num2 = temp;

  console.log(num1,num2)
}

swapWithTemp(2.34,3.45)

2) Использование сложения и вычитания

function swapWithPlusMinus(num1,num2){
  console.log(num1,num2)

  num1 = num1+num2;
  num2 = num1-num2;
  num1 = num1-num2;

  console.log(num1,num2)
}

swapWithPlusMinus(2.34,3.45)

Мы получаем сумму двух чисел на 4 строке. Сейчас, если мы вычтем одно число из суммы, то получим другое число. Это как раз то, что мы делаем на 5 строке. Вычитание num2 из суммы, которая находится в num1, дает нам изначальное значение num1, которое помещается в num2. Аналогично мы получаем num2 и помещаем его в num1 на 6 строке.

Осторожно: на просторах интернета гуляет еще один метод обмена в одну строчку лишь с операцией сложения.

Вот как он выглядит:

function swapWithPlusMinusShort(num1,num2){
  console.log(num1,num2)

  num2 = num1+(num1=num2)-num2;

  console.log(num1,num2)
}

swapWithPlusMinusShort(2,3)

Код выше дает ожидаемый результат. Выражение внутри () хранит num2 в num1, затем мы вычитаем num1 — num2, а это ноль, так как num2 — num2 = 0. Следовательно, мы получаем нужный результат. Но когда мы используем числа с плавающей точкой, иногда можем увидеть неожиданный результат.

function swapWithPlusMinusShort(num1,num2){
  console.log(num1,num2)

  num2 = num1+(num1=num2)-num2;

  console.log(num1,num2)
}

swapWithPlusMinusShort(2,3.1)

3) Использование сложения или вычитания

Мы можем получить тот же результат, используя только операцию сложения.

function swapWithPlus(num1,num2){
  console.log(num1,num2)

  num2 = num1 + (num1=num2, 0)

  console.log(num1,num2)
}

//Попробуйте реализовать это с помощью только вычитания
swapWithPlus(2.3,3.4)

Эта программа работает, но ее читабельность явно страдает. На 4 строке в скобках мы присваиваем num1 num2 и возвращаем 0. По сути наша строка выглядит так:

num2 = num1 + 0 => num2 = num1

Отсюда мы и получаем наш результат.

Замечание: некоторые движки JavaScript могут выполнять свои собственные оптимизации приведенного выше кода, который игнорирует + 0.

4) Использование умножения и деления

Принцип тот же, что и в предыдущем методе, но с парочкой «причудов».

function swapWithMulDiv(num1,num2){
  console.log(num1,num2)

  num1 = num1*num2;
  num2 = num1/num2;
  num1 = num1/num2;

  console.log(num1,num2)
}

swapWithMulDiv(2.34,3.45)

 Получаем произведения двух чисел и храним его в одном из чисел. Это то, что происходит на 4 строке. Затем делим произведение на второе число и получаем первое, потом повторяем процесс и получаем второе число.

Погодите! Что насчет тех «причудов»?

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

function swapWithMulDiv(num1,num2){
  console.log(num1,num2)

  num1 = num1*num2;
  num2 = num1/num2;
  num1 = num1/num2;

  console.log(num1,num2)
}

//Попробуйте обменять вот таки числа и посмотрите, что выйдет
swapWithMulDiv(2.34,0)

Наши значения не поменяются местами, и вместо этого мы получим NaN. Если вы посещали уроки математики, то помните, что вам всегда говорили не делить на 0, потому что получится неопределенность. Причина кроется в работе ограничений и в некоторых других вещах, в которые мы погружаться не будем. Сейчас же мы посмотрим на еще одну проблему этого метода.

Рассмотрим пример:

function swapWithMulDiv(num1,num2){
  console.log(num1,num2)

  num1 = num1*num2;
  num2 = num1/num2;
  num1 = num1/num2;

  console.log(num1,num2)
}
//Попробуйте обменять вот таки числа и посмотрите, что выйдет
swapWithMulDiv(2.34,Infinity)

Да, вновь NaN. Потому что мы не можем делить на бесконечность, это также неопределенность.

Хотите увидеть еще одну причуду? Я так и думал!

function swapWithMulDiv(num1,num2){
  console.log(num1,num2)

  num1 = num1*num2;
  num2 = num1/num2;
  num1 = num1/num2;

  console.log(num1,num2)
}

//Попробуйте обменять вот таки числа и посмотрите, что выйдет
swapWithMulDiv(2.34,-Infinity)

-бесконечность будет возвращать то же значение, как в прошлом примере, по той же причине.

Даже «математический фокусник» при всех своих силах не может сделать невозможного.

Ниже представлена краткая версия обмена с умножением и делением с теми же проблемами:

function swapWithMulDivShort(num1,num2){
  console.log(num1,num2)

  num2 = num1*(num1=num2)/num2;

  console.log(num1,num2)
}

swapWithMulDivShort(2.3,3.4)

Этот код аналогичен краткой версии обмена со сложением и вычитанием. Мы присваиваем num1 num2, а потом 4 строка выглядит так:

num2 = num1 * num2 / num2
=> num2 = num1

5) Использование умножения или деления

function swapWithMul(num1,num2){
  console.log(num1,num2)

  num2 = num1 * (num1=num2, 1)

  console.log(num1,num2)
}

//попробуйте сделать обмен через деление и возведение в степень
swapWithMul(2.3,3.4)

Эта программа работает, но ее читабельность явно страдает. На 4 строке в скобках мы присваиваем num1 num2 и возвращаем 1. На деле наша строка выглядит так:

num2 = num1 * 1 => num2 = num1

6) Использование побитового исключающего ИЛИ

XOR манипулирует битами. Возвращает 1, когда значения переменных различны, и 0 в противном случае.

XYX^Y
110
101
011
000

Теперь поймем, как это работает.

function swapWithXOR(num1,num2){
  console.log(num1,num2)

  num1 = num1^num2;
  num2 = num1^num2; 
  num1 = num1^num2;

  console.log(num1,num2)
}

// протестируйте также отрицательные значения
swapWithXOR(10,1)

4-ех битное представление 10 -> 1010

4-ех битное представление 1 -> 0001

Тогда:

На 4 строке: num1 = num1 ^ num2 => 1010 ^ 0001 => 1011 => 7 
На 5 строке: num2 = num1 ^ num2 => 1011 ^ 0001 => 1010 => 10
На 6 строке: num1 = num1 ^ num2 => 1011 ^ 1010 => 0001 => 1

Мы вновь поменяли значения местами.

Посмотрим другой пример.

function swapWithXOR(num1,num2){
  console.log(num1,num2)

  num1 = num1^num2;
  num2 = num1^num2;
  num1 = num1^num2;

  console.log(num1,num2)
}

swapWithXOR(2.34,3.45)

Мы получили только целую часть числа. И в этом проблема такого способа. XOR предполагает, что входные параметры это целые числа, и уже в соответствии с этим производит вычисления. Но числа с плавающей точкой не являются целыми и представлены стандартом IEEE 754, согласно которому число разбивается на 3 части: знаковый бит, группа битов, представляющих показатель степени, еще одна группа битов, представляющая число между 1 (включая) и 2 (не включая), то есть мантиссу. Поэтому мы и получаем не тот результат.

Еще один пример:

function swapWithXOR(num1,num2){
  console.log(num1,num2)

  num1 = num1^num2;
  num2 = num1^num2;
  num1 = num1^num2;

  console.log(num1,num2)
}
//Поэксперементируйте с целыми числами и бесконечностью.
swapWithXOR(-Infinity,Infinity)

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

7) Использование побитового исключающего НЕ ИЛИ

Эта операция противоположна исключающему ИЛИ. XNOR возвращает 0, если значения различны, и 1 в противном случае. В JavaScript нет специального оператора для XNOR, так что мы используем NOT оператор, чтобы инвертировать XOR.

XYXNOR
111
100
010
001

Давайте поймем, как это работает!

function swapWithXNOR(num1,num2){
  console.log(num1,num2)

  num1 = ~(num1^num2);
  num2 = ~(num1^num2);
  num1 = ~(num1^num2);

  console.log(num1,num2)
}

//Попробуйте отрицательные значения
swapWithXNOR(10,1)

4-ех битное представление 10 -> 1010

4-ех битное представление 1 -> 0001

На 4 строчке:

num1 = ~(num1 ^ num2) => ~(1010 ^ 0001) =>~(1011) => ~11 => -12

Так как мы получили отрицательное значение, нам необходимо конвертировать его в двоичное, инвертировать биты и прибавить единицу:

-12 => 1100 => 0011 + 1 => 0100

На 5 строчке:

num2 = ~(num1 ^ num2) => ~(0100 ^ 0001) => ~(0101) => ~5 => -6
-6 => 0110 => 1001 + 1 => 1010 => 10

На 6 строчке:

num1 = ~(num1 ^ num2) => ~(0100^ 1010) => ~(1110) => ~14 => -15
-15 => 1111 => 0000 + 1 => 0001 => 1

Это займет некоторое время, но мы получим обмен значений. Но, к сожалению, у этого способа будут те же проблемы, что и у метода с XOR. Все плохо с числами с плавающей точкой и бесконечностями.

8) Использование присваивания внутри массива

Вам просто нужна одна строка для обмена и, что более важно, никакой математики. Понадобятся лишь базовые знания о массивах. Может выглядеть странно, но оно работает.

Посмотрим на этот метод в действии!

function swapWithArray(num1,num2){
  console.log(num1,num2)

  num2 = [num1, num1 = num2][0];

  console.log(num1,num2)
}

swapWithArray(2.3,Infinity)

В нулевом индексе массива хранится num1, в 1 индексе мы присваиваем num1 num2 и, соответственно, храним num2. Затем мы просто обращаемся к 0 индексу, чтобы получить num1 и присвоить это значения num2. Таким образом мы может менять и целые числа, и числа с плавающей точкой, и бесконечности, и даже строки. Выглядит довольно аккуратно, но мало читабельно. Взглянем на похожий способ.

9) Использование деструктуризации

Это нововведение, появившееся в ES6. И оно очень простое. В одну линию мы можем поменять значения местами:

let num1 = 23.45;
let num2 = 45.67;

console.log(num1,num2);

[num1,num2] = [num2,num1];

console.log(num1,num2);

10) Использование немедленно вызываемой функции (IIFE)

Еще один странный вариант обмена. IIFE — это функция, которая вызывается сразу после ее объявления.

Посмотрим, как мы можем ее использовать:

function swapWithIIFE(num1,num2){
  console.log(num1,num2)

  num1 = (function (num2){ return num2; })(num2, num2=num1)

  console.log(num1,num2)
}

swapWithIIFE(2.3,3.4)

В этом примере мы сразу же вызываем функцию на 4 строке. Скобки в конце это аргументы. Во втором аргументе мы присваиваем num2 num1, а первый просто возвращается из функции. Наши значения вновь поменялись местами. Но имейте в виду, этот способ не эффективный.