递归优化(尾调用优化)

166 阅读2分钟

为什么需要尾调用优化?

function outerFunction() {
  return innerFunction()
}

在Es6之前,上面例子在内存中会发生如下操作

  1. 执行到outerFunction函数体,第一个栈帧被推到栈上.
  2. 执行到outerFunction函数return语句.必须先计算innerFunction函数.
  3. 执行innerFunction函数体,第二个栈帧被推到栈上.
  4. 执行innerFunction函数,计算返回值.
  5. 将返回值传给outerFunction,然后ounterFunction在返回值.
  6. 将栈帧弹出栈外.

Es6以后所作优化

  1. 执行到outerFunction函数体,第一个栈帧被推到栈上.
  2. 执行到outerFunction函数return语句.必须先计算innerFunction函数.
  3. 引擎发现把第一个栈帧弹出栈外也没问题,因为outerFunction的返回值也是innerFunction的返回值
  4. 将outerFunction弹出栈外
  5. 执行到innerFunction函数体,栈帧被推到栈上.
  6. 执行innerFunction函数,计算返回值
  7. 将innerFunction的栈帧弹出栈外

第二种相比较第一种的优化点

第一种情况下,每次多调用一次嵌套函数,就会多增加一个栈帧.而第二种,无论调用多少次嵌套函数,都只有一个栈帧.

怎样才算是尾调用优化?

  1. 代码在严格模式下执行
  2. 外部函数的返回值是对尾部函数的调用
  3. 尾调用函数返回后不需要执行额外的逻辑
  4. 尾调用函数不是应用外部函数作用域中自由变量的闭包

同时满足以上条件才能称为尾调用优化

错误例子

"use strict"
// 无优化,尾调用没有返回
function outerFunction() {
  innerFunction()
}

// 无优化,尾调用没有直接返回
function outerFunction() {
  let innerFunctionResult = innerFunction()
  return innerFunctionResult
}

// 无优化,尾调用是一个闭包
function outerFunction() {
  let foo = "bar"
  function = innerFunction() {
    return foo
  }
  return innerFunction()
}

// 无优化,返回后需要执行额外的逻辑,转字符串
function onterFunction() {
  return innerFunction().toString()
}

再来几个正确的例子

"use strict"

// 有优化: 栈帧销毁钱执行参数计算
function onterFunction(a , b) {
  return innerFunction(a + b)
}

// 有优化: 初始返回值不设计栈帧
function outerFunction(a, b) {
  if (a < b) {
    return a
  }
  return innerFunction(a + b)
}

(学习笔记:JavaScript高级程序设计第四版---10.13 尾调用优化)

想详细了解的小伙伴可以自己去翻阅