Follow the front-end small Ou , read more original technical articles
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 ofarguments
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 thereturn
statement, and calculate the return value first to calculateinnerFunction
- 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
, thenouterFunction
and then return the value - Pop the stack frame off the stack
- Execute to the
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 toreturn
statement, calculate the return value first calculateinnerFunction
- Because
innerFunction
return value isouterFunction
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
- Execute to the
- 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
andf.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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。