Follow the front-end small Ou , read more original technical articles

related code →

10.12 Recursion

  • recursive function is a function by name calling themselves
function factorial(num) {
  if (num <= 1) {
    return 1
  } else {
    return num * factorial(num - 1)
  }
}
  • The function logic and function name are lotus root and , so after assigning the recursive function to other variables and removing the relationship between the original function name and the function, an error will be reported
let anotherFactorial = factorial // 访问指针
factorial = null // 切断factorial与函数之间的联系
console.log(anotherFactorial(4)) // TypeError: factorial is not a function
  • Use arguments.callee to decoupling ( arguments.callee points to the function of arguments
function factorial2(num) {
  if (num <= 1) {
    return 1
  } else {
    return num * arguments.callee(num - 1)
  }
}
let anotherFactorial2 = factorial2 // 访问指针
factorial2 = null // 切断factorial与函数之间的联系
console.log(anotherFactorial2(4)) // 24,arguments.callee指向anotherFactorial2
  • Strict mode cannot access argumengts.callee (error will be reported), use named function expression to achieve the goal
let factorial3 = function f(num) {
  if (num <= 1) {
    return 1
  } else {
    return num * f(num - 1) // 无论赋值给哪个变量,表达式f都不会变
  }
}
let anotherFactorial3 = factorial3
factorial3 = null
console.log(anotherFactorial3(4)) // 24

10.13 Tail call optimization

  • ES6 is ideal for new tail call of memory management optimization mechanism that return value of the external function is a return value of the function of internal
function outerFunction() {
  return innerFunction() // 尾调用
}
  • Before ES6 optimization, this example will perform the following operations in memory ( calls an additional nested function, one more stack frame ):

    • Execute to the outerFunction , and the first stack frame is pushed onto the stack
    • Execute the outerFunction function body to the return statement, and calculate the return value first to calculate innerFunction
    • Execute to the innerFunction function 06114729e5e627, and the second stack frame is pushed onto the stack
    • Execute the innerFunction and calculate the return value
    • Return the return value to outerFunction , then outerFunction and then return the value
    • Pop the stack frame off the stack
  • After ES6 optimization, this example will operate as follows in the memory ( no matter how many times the nested function is called, there is only one stack frame ):

    • Execute to the outerFunction function, and the first stack frame is pushed onto the stack
    • Execute outerFunction function body to return statement, calculate the return value first calculate innerFunction
    • Because innerFunction return value is outerFunction return value, thus engine can find the first stack frame popped outer
    • Pop the stack frame of outerFunction
    • Execute to the innerFunction function 06114729e5e75a, and the second stack frame is pushed onto the stack
    • Execute the innerFunction and calculate the return value
    • innerFunction the stack frame of 06114729e5e7aa out of the stack
  • ES6 tail call optimization key : If the logic of the function allows it to be destroyed based on the tail call, there will be so many engines

10.13.1 Conditions for tail call optimization

  • It is necessary to make sure that the external stack frame does not need to exist:

    • The outer code needs to be executed strict mode

      • f.arguments and f.caller are allowed in non-strict mode will reference the stack frame of the external function
    • The return value of the external function is a call to the tail calling function
    • After the tail call function returns, no additional logic is required
    • The tail call function is not a closure that refers to a free variable in the scope of an external function
;('use strict')

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

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

function outerFunction() {
  return innerFunction().toString() // 无优化,尾调用返回后必须转型为字符串(有额外逻辑)
}

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

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

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

function outerFunction(condition) {
  return condition ? innerFunctionA() : innerFunctionB() // 有优化,2个内部函数都在尾部
}

10.13.1 Tail call optimized code

  • Tail call optimization recursive scene , because recursive code is the easiest to quickly generate a large number of stack frames in the stack memory
  • Take Fibonacci sequence as an example, use tail call optimization to reduce the burden on the browser:
function fib(n) {
  if (n < 2) {
    return n
  }
  return fib(n - 1) + fib(n - 2) // 返回语句有相加操作,不符合优化条件
}

// 使用2个嵌套的函数重构斐波那契数列
;('use strict')

function fib(n) {
  return fibImp1(0, 1, n) // 以0为初始值,1作为第1个数字,n为第几个数字
}
function fibImp1(a, b, n) {
  if (n === 0) return a
  return fibImp1(b, a + b, n - 1) // 外部函数的返回值是内部函数的返回值,符合优化条件
}

Summary & Questions

  • What is a recursive function? Write a piece of code to represent factorial, requiring decoupling of function logic and function name
  • In strict mode, write a piece of code to represent factorial, requiring that the function name can be assigned to any variable
  • What is tail call optimization? How does it optimize memory management? What are the conditions for tail call optimization?
  • Write a piece of code, use tail recursion to optimize the calculation of Fibonacci sequence

小讴
217 声望16 粉丝