尾调用(Tail Call)是函数式编程的一个重要概念 一定是在最后一步执行
尾调优化
"尾调用优化"(Tail call optimization),即只保留内层函数的调用记录。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用记录只有一项,这将大大节省内存。这就是"尾调用优化"的意义
应用
-
尾递归 正常递归会有一系列的调用记录在内存中,尾递归则每调用一次就会产出一次;很容易发生"栈溢出"错误(stack overflow)
function factorial(n) { if( n==1) return 1; return n*factorial(n-1) }
上面的就可能出现stack oveflow,经过优化
function factorial(n,total){ if(n === 1) rerturn total; return factorial(n-1,n*total) }
由此可见,"尾调用优化"对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6也是如此,第一次明确规定,所有 ECMAScript 的实现,都必须部署"尾调用优化"。这就是说,在 ES6 中,只要使用尾递归,就不会发生栈溢出,相对节省内存。
-
递归改写 方法:就是把所有用到的内部变量改写成函数的参数。比如上面的例子,阶乘函数 factorial 需要用到一个中间变量 total ,那就把这个中间变量改写成函数的参数。
function factorial(n){ tail(n,1) } function tail(n,total){ if(n==1) return total; return tail(n-1,n*total) }
curry化 也可以这样改造的 什么是curry化呢,意思是将多参数的函数转换成单参数的形式。这里也可以使用柯里化。
function curry(fn,n) { return function(m) { fn.call(this,m,n); } } function tail(n,total){ if(n==1) return total; return tail(n-1,n*total) } let exec = curry(tail,1) es6 模式更简单 function factorial(n,total=1) { …… }