一切的起源来自以下递归代码
function sum(x, y) {
if (y > 0) {
return sum(x + 1, y - 1);
} else {
return x;
}
}
sum(1, 100000)
// Uncaught RangeError: Maximum call stack size exceeded(…)
众所周知,递归调用函数多次,浏览器会报错栈溢出。
这里纠正一个说法,栈溢出不是只在自己调自己的情况下才会出现的。栈溢出本质是栈空间被填满了,也就是说不断的调用新的函数,创建新的栈帧,且旧的栈帧没有被清除掉,达到了浏览器设定的峰值就会抛出的错误。
也就是说不论是相同的函数还是不同的函数递归调用,只要不断的创建新的栈帧,并且让浏览器无法清除旧的栈帧就可以达到让浏览器抛出栈溢出错误的目的。
以下是我瞎写的测试代码,以佐证我上述观点,有兴趣的可以试下。
const j = 0;
window[`fn${j}`] = (j) => {
console.log(j);
if (j < 10000) {
++j;
window[`fn${j}`] = window[`fn${--j}`];
window[`fn${j}`](j);
}
};
fn0(j);
最近在读阮一峰老师写的es6入门,看到了尾调用相关的优化。读到了蹦床函数这样一段代码,所以才有了疑惑,为什么这样写就可以避免栈溢出?
下面是阮一峰老师写的蹦床函数,我运行了一遍,发现如果我去掉bind函数的使用,就是会报出栈溢出的错误,由此可见蹦床函数实现尾调用优化的关键在于bind。
function trampoline(f) {
while (f && f instanceof Function) {
f = f();
}
return f;
}
function sum(x, y) {
if (y > 0) {
return sum.bind(null, x + 1, y - 1);
} else {
return x;
}
}
trampoline(sum(1, 100000))
// 100001