# 学习Javascript之尾调用

## 前言

• 参考文章：尾递归的后续探究
• 公众号：「前端进阶学习」，回复「666」，获取一揽子前端技术书籍

## 正文

### 尾调用

``````function add(x, y) {
return x + y;
}
function sum() {
}

``````function add(x, y) {
return x + y;
}
// 情况1
function sum() {
}
// 情况2
function sum2() {
return a;
}

### 尾递归

``````function sum(n) {
if (n <= 1) return 1;
return sum(n - 1) + n;
}
sum(10000); // 50005000

``````function sum(n, result = 1) {
if (n <= 1) return result;
return sum(n - 1, result + n);
}
sum(10000); //  Maximum call stack size exceeded

### 尾调用优化

``````function C() {}
function B() { return C(); }
function A() { return B(); }
A();

**尾调用优化：**对符合要求的尾调用函数，只在执行栈中保存最内层函数的执行上下文的一种实现。

``````'use strict';
function sum(n, result = 1) {
if (n <= 1) return result;
return sum(n - 1, result + n);
}
sum(10000);

``````RangeError: Maximum call stack size exceeded
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:4:13)
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:6:10)
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:6:10)
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:6:10)

``````\$ node --harmony_tailcalls tail-call.js
5000050000

``````'use strict';
function sum(n, result = 1) {
console.trace();
if (n <= 1) return result;
return sum(n - 1, result + n);
}
const result = sum(3);
console.log(result);

``````Trace
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:3:13)
at Object.<anonymous> (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:7:16)
Trace
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:3:13)
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:5:10)
at Object.<anonymous> (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:7:16)
Trace
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:3:13)
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:5:10)
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:5:10)
at Object.<anonymous> (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:7:16)

``````Trace
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:3:13)
at Object.<anonymous> (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:7:16)
Trace
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:3:13)
at Object.<anonymous> (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:7:16)
Trace
at sum (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:3:13)
at Object.<anonymous> (/Users/mac/Desktop/demo/html-css-js-demo/tail-call.js:7:16)

### 解决堆栈溢出报错

1. `for`循环。根本原因是执行上下文太多导致的爆栈，那么不调用函数自然可以解决这个问题：
``````'use strict';
function sum(n) {
let res = 0;
for (var i = 0; i < n; i++) {
res += i;
}
return res;
}
const result = sum(3);
console.log(result);

1. 某些情况下确实无法使用`for`循环，还是要调用函数，此时可以利用弹跳床函数，所谓弹跳床函数，相当于函数的一个中转站。
``````// 弹跳床函数，执行函数，如果函数返回类型还是函数则继续执行，直到执行结束
function trampoline(f) {
while (f && f instanceof Function) {
f = f();
}
return f;
}

``````function sum(n, result = 1) {
if (n <= 1) return result;
return sum.bind(null, n - 1, result + n);
}

``````trampoline(sum(100000));

### 尾调用优化默认关闭

1. 隐式优化问题。由于引擎消除尾递归是隐式的，函数是否符合尾调用而被消除了尾递归很难被程序员自己辨别；
2. 调用栈丢失问题。尾调用优化要求除掉尾调用执行时的调用堆栈，这将导致执行流中的堆栈信息丢失。

Chrome下使用尾递归写法的方法依旧出现调用栈溢出的原因在于：

• `直接原因`： 各大浏览器（除了safari）根本就没部署尾调用优化；
• `根本原因`： 尾调用优化依旧有隐式优化和调用栈丢失的问题；