《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(); }
具体执行逻辑:
- 初始调用
branch(60, 0.5, 0.8),绘制主干;因60≥8,保存Canvas状态,完成平移+旋转后,调用branch(48, 0.5, 0.8)。 - 此时原
branch(60,...)的执行停在第一个branch(...)调用处,进入新的递归层级。 - 这个新递归会重复上述步骤,直到某次调用的
length<8(比如最终length降到6.4左右),触发return,该子递归执行完毕,返回上一层函数。 - 回到上一层函数的暂停位置(第一个
branch(...)之后),开始执行cx.rotate(2*angle),随后调用第二个branch(...),再次进入递归层级。 - 等第二个子递归全部执行完毕返回后,才会执行当前函数的最后一行
cx.restore(),随后当前函数结束,回到更上一层的暂停位置继续执行。
简言之:cx.restore()是当前递归层级的最后一步,但它的执行要等两个子递归分支都完全跑完才会触发。你在调试中看到回到cx.rotate(2*angle),是因为第一个子递归已经执行完毕,回到了父函数的暂停点,还没轮到执行cx.restore()。
补充:Canvas save/restore的作用
cx.save()用于保存当前Canvas的变换状态(平移、旋转等),cx.restore()则恢复到保存的状态,这部分仅用于隔离不同分支的变换影响,和递归的执行流程逻辑无关。
内容的提问来源于stack exchange,提问作者feinNotiz
相关产品推荐
相关产品推荐

