JavaScript中交换变量a/b的多种方法

简介

问题是有一个变量ab要怎么交换它们的值,有多少种方法,那种方法比较好比较省时省力。我们尽量使用最少的代码和内存空间来实现变量的交换。
下面我们分别使用五种类型的方法实现变量交换,它们都有自己的优缺点,五种方法如下:

  • 借助临时变量
  • ES实现方法
  • 通过加减法
  • 按位异或
  • 利用逗号操作符

借助临时变量

首先来一个最简单的实现方式代码如下:

1
2
3
4
5
6
7
var a = 100;
var b = 200;
// 临时变量
var temporary = b;
b = a;
a = temporary;
temporary = null;

这个我们多使用了一个变量temporary,这种方法也是我们经常用的。如果我们不声明一个临时变量怎么实现呢。

本方法优点是实现简单,缺点是多声明一个变量,在执行期间多占用内存,并且要记得在最后执行完成记得清空变量

ES 实现方法

我们可以使用ES6中的解构特性,这个应该也是比较常用的方法。但是兼容性没有上面的方法好,但是在VueReact中经常用到。

1
2
3
var a = 100;
var b = 200;
[a, b] = [b, a];

本方法优点代码简洁并且没有声明多余变量,缺点是兼容性并不太理想

如果不使用ES6中的特性,实现代码如下:

1
2
3
4
5
var a = 100;
var b = 200;
a = { a: b, b: a };
b = a.b;
a = a.a;

我们通过把a设置为一个对象用来保存ab的值,然后再分别取出。通过把对象替换为数组也是可以实现,这里就不做演示了。

本方法优点并且没有声明多余变量,缺点是改变变量的类型在执行期间多使用内存

通过加减法

通过加减法也是可以实现的,首先是通过加法实现,代码实现如下:

1
2
3
4
5
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = a + b; // a 的值为 a(100) + b(200) = 300
b = a - b; // b 为 a(300) - b(200) 为 100
a = a - b; // 因为在这次执行的时候b已经为上面的值100 a 为 a(300) - b(100) 为 200

但是这种方式可能会导致数字溢出,所以我们可以通过减法来实现更安全。下面看代码实现:

1
2
3
4
5
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = a - b; // a 的值为 a(100) - b(200) = -100
b = b + a; // b 为 a(-100) + b(200) 为 100
a = b - a; // 因为在这次执行的时候b已经为上面的值100 a 为 b(100) - a(-100) 为 200

加法的实现更好理解,但是存在整数溢出的风险;减法的实现不太好理解,但是并不会存在整数溢出的风险

按位异或

首先我们要了解一下什么是按位异或,它的定义是按位异或(XOR):a ^ b对于每一个比特位(二进制 base 2),当两个操作数相应的比特位(二进制 base 2)有且只有一个1时,结果为1,否则为0。

a b a XOR b
0 0 0
0 1 1
1 0 1
1 1 0

可能看到上面还是有点懵逼,那么可以看我另外一篇的博客了解 JS 中的位运算符
我们通过上面的表格知道a ^ a0,那么a ^ a ^ b 就相当于0 ^ b的出来的值为b的值。代码如下实现如下:

1
2
3
4
5
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = a ^ b;
b = a ^ b;
a = a ^ b;

按位异或:实现方式最好,直接通过二进制对比实现、代码简洁,但是转换过程不可知

利用逗号操作符

也可以通过一些特殊的技巧来实现,逗号操作符结合()或者[]来实现交换位置。

我们简单了解一下逗号操作符:对它的每个操作数求值(从左到右),并返回最后一个操作数的值。,来一个简单的实例:

1
2
3
4
5
6
7
8
var x = 1; // 初始值 x = 1
x = [x++, x + 1][1]; // 3
// 1. 因为有`=`复制符 要从右面开始执行,根据逗号操作符的定义我们先执行 x++; x = 2
// 2. 再执行 x + 1; x = 3
// 3. 再通过[2, 3][1]提取数组下标为1的值 x = 3

var y = 1; // 初始值 y = 1
y = y + (0, y++); // 1

我们以对象为示例大致分析一下它的执行顺序,以便于更好的理解JavaScript的执行顺序:

  1. 声明变量y,并且给y赋值为1
  2. ()的优先级是最高的,然后是++ > +优先级次之,再是= > ,。执行优先级如下表格所示。
  3. 所以会先执行外部的(), 执行内部的代码,遇到,符从左开始执行(0, (y++)),遇到内部的(y++)这个时候因为()的优先比++的优先级高,所以执行结果就是(0, 1)。再执行,符他返回了一个1.
  4. 再执行外部的y = y + 1,它们的优先级为+ > =,先执行y + 1再执行y = 2,所以最后y2
优先级 运算类型 关联性 运算符
20 圆括号 n/a(不相关) ( … )
17 后置递增(运算符在后) n/a … ++
16 一元加法 从右到左 + …
3 一元加法 从右到左 … = …
0 赋值 从左到右 … , …

下面我们就通过逗号操作符来实现两值交换,通过数组实现代码如下:

1
2
3
4
5
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = [b, (b = a)][0];
// a 200
// b 100

方法()实现代码如下:

1
2
3
4
5
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = b + ((b = a), 0);
// a 200
// b 100

优点实现简洁,更灵活,数组的操作还是容易理解一点,但是”,”操作符不太好了解

总结

通过上面 5 种方法我们开扩了自己的思路,即复习了ES6也了解执行优先级按位操作符,等等。所以是比较有趣的,如果你有更好的解法请留言,大家一起进步。个人认为最好的方法是按位操作符减法实现是比较好的实现方法。