关于JavaScript递归中shift()与while循环的执行逻辑疑问
解释这段带
shift()的JavaScript递归代码执行结果 首先先把你的代码贴出来方便对照:
function combine(nums) { while (nums.length) { let r = nums.shift(); console.log(r, ':', nums); combine(nums.slice(0)); } } combine([1,2,3])
咱们一步步拆解执行流程,核心要搞懂两个关键细节:shift()会直接修改原数组,nums.slice(0)是创建原数组的独立副本(和原数组不是同一个引用,修改副本不会影响原数组)。
第一步:初始调用combine([1,2,3])
此时函数里的nums是[1,2,3],满足while循环条件:
- 执行
nums.shift(),取出第一个元素1,原数组nums直接变成[2,3],打印1 : [ 2, 3 ] - 调用
combine(nums.slice(0)),也就是把当前的[2,3]复制一份,传给新的combine调用,进入下一层递归。
第二步:递归调用combine([2,3])
这里的nums是全新的数组[2,3],进入while循环:
nums.shift()取出2,原数组变成[3],打印2 : [ 3 ]- 调用
combine([3].slice(0)),复制[3]传给下一层递归。
第三步:递归调用combine([3])
这里的nums是[3],进入while循环:
nums.shift()取出3,原数组变成[],打印3 : []- 调用
combine([].slice(0))也就是combine([]),这个调用里nums.length为0,while循环直接跳过,函数返回,回到上一层。
现在回到combine([3])的while循环,此时nums已经是[],循环条件不满足,函数返回,回到combine([2,3])。
第四步:回到combine([2,3])的循环
注意!这里的nums还是之前的[3](因为之前只shift掉了2,剩下[3],而递归操作的是副本,完全不影响这个原数组)。所以while循环继续检查nums.length,此时是1,满足条件:
nums.shift()取出3,原数组变成[],打印3 : []- 调用
combine([]),直接返回,然后nums是[],循环结束,函数返回,回到最开始的combine([1,2,3])。
第五步:回到初始combine([1,2,3])的循环
这里的nums是[2,3](之前只shift掉了1,剩下[2,3],递归操作的是副本,不影响这个数组)。while循环继续执行:
nums.shift()取出2,原数组变成[3],打印2 : [ 3 ]- 调用
combine([3].slice(0)),进入新的递归层。
第六步:再次递归combine([3])
和之前的combine([3])流程完全一致:
shift()取出3,数组变[],打印3 : []- 调用
combine([])返回,循环结束,函数回到初始调用的循环。
第七步:初始调用的最后一次循环
此时nums是[3],while循环条件满足:
nums.shift()取出3,数组变[],打印3 : []- 调用
combine([])返回,循环结束,整个函数执行完毕。
这样一步步走下来,就得到了你看到的输出结果。核心逻辑就是:每次递归传的是当前数组的副本,不会打扰原递归层的数组;而shift()是直接修改原数组,所以当递归返回后,原递归层的while循环会继续处理剩下的数组元素。
内容的提问来源于stack exchange,提问作者David S Lee
相关产品推荐
相关产品推荐

