使用到了[nums[i], nums[nums[i] - 1]] = [nums[nums[i] - 1], nums[i]];
本意是交换两处下标的值,却发现执行一直死循环,调试了一下发现问题出现交换这里。
考虑数组[3, 4, -1, 1],在执行到i=0时,想要交换nums[0]和nums[2],本意是要将数组变为[-1,4,3,1]
结果交换后数组变为了:
很明显越界了,我将[nums[i], nums[nums[i] - 1]] = [nums[nums[i] - 1], nums[i]]换为 const temp = nums[i]; nums[i] = nums[temp - 1]; nums[temp - 1] = temp;后,代码正常工作了
平常都是将[a,b] = [b,a]当做const temp = a;a = b;b = temp;的语法糖使用,以为两者时等价的,却在这里出了问题。
那么就开始研究一下解构赋值:
在 JavaScript 中,解构赋值的右侧表达式会先求值,但左侧的赋值是“从左到右”进行的。
对于 [nums[i], nums[nums[i] - 1]] = [nums[nums[i] - 1], nums[i]],实际执行顺序如下:
-
先计算右侧的
[nums[nums[i] - 1], nums[i]]:- 先计算
nums[nums[i] - 1](记作value1)。 - 再计算
nums[i](记作value2)。 - 此时右侧为
[value1, value2]。
- 先计算
-
然后对左侧赋值:
- 先赋值
nums[i] = value1(这会直接修改nums[i])。 - 再赋值
nums[nums[i] - 1] = value2,但此时nums[i]已经被修改,导致nums[i] - 1可能不再是原来的索引!
- 先赋值
上面的示例为什么出错
nums = [3, 4, -1, 1],i = 0(nums[0] = 3):
-
右侧计算:
nums[nums[0] - 1] = nums[3 - 1] = nums[2] = -1nums[0] = 3- 右侧为
[-1, 3]。
-
左侧赋值:
- 先执行
nums[0] = -1,此时nums变为[-1, 4, -1, 1]。 - 再执行
nums[nums[0] - 1] = 3,即nums[-1 - 1] = nums[-2] = 3(越界访问!)。
- 先执行
问题出现:nums[i] 被修改后,nums[i] - 1 可能变成一个无效索引(如 -2),导致后续赋值出错或死循环。