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 в противном случае.
X | Y | X^Y |
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
Теперь поймем, как это работает.
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.
X | Y | XNOR |
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 1 |
Давайте поймем, как это работает!
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, а первый просто возвращается из функции. Наши значения вновь поменялись местами. Но имейте в виду, этот способ не эффективный.