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

《Eloquent JavaScript》第17章递归函数cx.restore()执行流程疑问

递归执行流程与Canvas状态恢复的困惑解答

核心原因:递归调用栈的暂停与恢复

你误以为cx.restore()作为函数最后一行会直接终止执行,但实际上递归调用会让当前函数的执行暂时挂起,只有当子递归调用完全执行完毕后,才会回到挂起的位置继续执行后续代码。

结合代码拆解执行步骤

先看核心递归函数:

function branch(length, angle, scale) {
  debugger;
  cx.fillRect(0, 0, 1, length);
  if (length < 8) return;
  cx.save();
  cx.translate(0, length);
  cx.rotate(-angle);
  // 第一个递归调用:当前函数执行暂停,进入子递归
  branch(length * scale, angle, scale);
  // 子递归全部完成后,才会执行到这一行
  cx.rotate(2 * angle);
  // 第二个递归调用:当前函数再次暂停,进入子递归
  branch(length * scale, angle, scale);
  // 第二个子递归完成后,才执行restore
  cx.restore();
}

具体执行逻辑:

  1. 初始调用branch(60, 0.5, 0.8),绘制主干;因60≥8,保存Canvas状态,完成平移+旋转后,调用branch(48, 0.5, 0.8)。
  2. 此时原branch(60,...)的执行停在第一个branch(...)调用处,进入新的递归层级。
  3. 这个新递归会重复上述步骤,直到某次调用的length<8(比如最终length降到6.4左右),触发return,该子递归执行完毕,返回上一层函数。
  4. 回到上一层函数的暂停位置(第一个branch(...)之后),开始执行cx.rotate(2*angle),随后调用第二个branch(...),再次进入递归层级。
  5. 等第二个子递归全部执行完毕返回后,才会执行当前函数的最后一行cx.restore(),随后当前函数结束,回到更上一层的暂停位置继续执行。

简言之:cx.restore()是当前递归层级的最后一步,但它的执行要等两个子递归分支都完全跑完才会触发。你在调试中看到回到cx.rotate(2*angle),是因为第一个子递归已经执行完毕,回到了父函数的暂停点,还没轮到执行cx.restore()。

补充:Canvas save/restore的作用

cx.save()用于保存当前Canvas的变换状态(平移、旋转等),cx.restore()则恢复到保存的状态,这部分仅用于隔离不同分支的变换影响,和递归的执行流程逻辑无关。

内容的提问来源于stack exchange,提问作者feinNotiz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 12:32:53