简介
问题是有一个变量a、b要怎么交换它们的值,有多少种方法,那种方法比较好比较省时省力。我们尽量使用最少的代码和内存空间来实现变量的交换。
下面我们分别使用五种类型的方法实现变量交换,它们都有自己的优缺点,五种方法如下:
借助临时变量ES实现方法通过加减法按位异或利用逗号操作符
借助临时变量
首先来一个最简单的实现方式代码如下:
var a = 100;
var b = 200;
// 临时变量
var temporary = b;
b = a;
a = temporary;
temporary = null;
这个我们多使用了一个变量temporary,这种方法也是我们经常用的。如果我们不声明一个临时变量怎么实现呢。
本方法优点是实现简单,缺点是多声明一个变量,在执行期间多占用内存,并且要记得在最后执行完成记得清空变量
ES 实现方法
我们可以使用ES6中的解构特性,这个应该也是比较常用的方法。但是兼容性没有上面的方法好,但是在Vue、React中经常用到。
var a = 100;
var b = 200;
[a, b] = [b, a];
本方法优点代码简洁并且没有声明多余变量,缺点是兼容性并不太理想
如果不使用ES6中的特性,实现代码如下:
var a = 100;
var b = 200;
a = { a: b, b: a };
b = a.b;
a = a.a;
我们通过把a设置为一个对象用来保存a、b的值,然后再分别取出。通过把对象替换为数组也是可以实现,这里就不做演示了。
本方法优点并且没有声明多余变量,缺点是改变变量的类型在执行期间多使用内存
通过加减法
通过加减法也是可以实现的,首先是通过加法实现,代码实现如下:
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
但是这种方式可能会导致数字溢出,所以我们可以通过减法来实现更安全。下面看代码实现:
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 ^ a为0,那么a ^ a ^ b 就相当于0 ^ b的出来的值为b的值。代码如下实现如下:
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = a ^ b;
b = a ^ b;
a = a ^ b;
按位异或:实现方式最好,直接通过二进制对比实现、代码简洁,但是转换过程不可知
利用逗号操作符
也可以通过一些特殊的技巧来实现,逗号操作符结合()或者[]来实现交换位置。
我们简单了解一下逗号操作符:对它的每个操作数求值(从左到右),并返回最后一个操作数的值。,来一个简单的实例:
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的执行顺序:
- 声明变量
y,并且给y赋值为1 ()的优先级是最高的,然后是++>+优先级次之,再是=>,。执行优先级如下表格所示。- 所以会先执行外部的
(), 执行内部的代码,遇到,符从左开始执行(0, (y++)),遇到内部的(y++)这个时候因为()的优先比++的优先级高,所以执行结果就是(0, 1)。再执行,符他返回了一个1. - 再执行外部的
y = y + 1,它们的优先级为+ > =,先执行y + 1再执行y = 2,所以最后y为2。
| 优先级 | 运算类型 | 关联性 | 运算符 |
|---|---|---|---|
| 20 | 圆括号 | n/a(不相关) | ( … ) |
| 17 | 后置递增(运算符在后) | n/a | … ++ |
| 16 | 一元加法 | 从右到左 | + … |
| 3 | 一元加法 | 从右到左 | … = … |
| 0 | 赋值 | 从左到右 | … , … |
下面我们就通过逗号操作符来实现两值交换,通过数组实现代码如下:
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = [b, (b = a)][0];
// a 200
// b 100
方法()实现代码如下:
var a = 100; // 初始值 a
var b = 200; // 初始值 b
a = b + ((b = a), 0);
// a 200
// b 100
优点实现简洁,更灵活,数组的操作还是容易理解一点,但是","操作符不太好了解
总结
通过上面 5 种方法我们开扩了自己的思路,即复习了ES6也了解执行优先级、按位操作符、,等等。所以是比较有趣的,如果你有更好的解法请留言,大家一起进步。个人认为最好的方法是按位操作符、减法实现是比较好的实现方法。