You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:43:30