尾调
尾调就是指某个函数的最后一步是返回另一个函数的调用结果。
如function(){return ickt()} ickt就是尾调函数
//子函数
function ickt(){
console.log(this)
}
//父函数
function demo(){
return ickt()
}
// 执行父函数
demo()
不是尾调的几种情况
// //子函数
function ickt(){
console.log(this)
}
// 以下几种形式不是尾调
function demo(){
let a = ickt()
var b = 2
return a
}
//执行demo
demo()
function demo(){
ickt()
var b = 2;
}
//执行demo
demo()
function demo(){
return ickt()+1;
}
demo()
尾调优化:
闭包
//闭包
function demo(){
let a =1;
let b =2;
function inner(){
console.log(a+b)
}
return inner
}
demo()()
尾递归
// 尾递归优化
function add(n, total=0) {
//如果等于1
if (n === 1) {
return 1 + total;
}
return add(n - 1, n + total)
}
console.log(add(10, 0))
// 函数柯理化 解决 函数默认参数问题
// 尾递归优化
function add(n, total) {
//如果等于1
if (n === 1) {
return 1 + total;
}
return add(n - 1, n + total)
}
// console.log(add(10, 0))
//函数柯理化 传递函数和参数 同时执行这个函数和参数并返回结果
let curry = function(fn,...args){
return function(...args2){
return fn(...args2,...args)
}
}
//传递默认参数
let ickt = curry(add,0)
//执行
console.log(ickt(10))
蹦床函数
解决堆栈溢出问题 但是并没有解决尾递归优化问题,只是间竭解决堆栈溢出问题 不是不停改函数名称
// 蹦床函数
function trampoline(f){
// 转成函数
while(f&& typeof f === "function"){
//执行不返回函数
f =f();
}
//返回结果
return f
}
// 返回新的方法 使用bind
// // 尾递归优化
function add(n, total=0) {
//如果等于1
if (n === 1) {
return 1 + total;
}
// 返回新的方法 并且不会执行
return add.bind(null,n - 1, n + total)
}
//通过蹦床函数 解决堆栈溢出问题
console.log(trampoline(add(100000)))
递归变循环 不要总执行一个函数
function ickt(f) {
//定义结果
let result;
//定义状态
let status = false;
//定义参数用于缓存
let args = [];
return function inner() {
// 缓存传递的参数
args.push(arguments);
//如果没有启动,启动尾递归优化
if (!status) {
status = true;
//如果有参数
while (args.length) {
//存储结果
result = f.apply(null, args.shift())
}
// 关闭优化
status = false;
//返回结果
return result
}
}
}
//定义add方法
let add = ickt(function sum(n, total) {
console.trace()
//如果是1
if (n === 1) {
return 1 + total
}
return add(n - 1, n + total)
})
console.log(add(10, 0))
函数调用的时候,会在内存中创建一个"调用记录"又称“调用帧“,保存调用位置和内部变量等信息。如果在父函数内部调用函数,那么在符函数调用帧得到下面会创建子函数的调用帧,等到子函数运行结束,将结果返回父函数,父函数调用帧才会消失,如果在子函数的内部还调用了其他后代函数,那就还有后代函数的调用帧,一次类推,所有的调用帧就会形成一个”调用栈”。
尾调用由于是函数的最后一步操作,所以不需要保留外层(父)函数调用帧,因为调用位置,内部变量等信息都不会再用到,直接用内层(子函数)的调用帧取代外层(父)函数的即可。
注意:ES6的尾调再优化只在严格模式下开启,正常模式下是无效的
。
注意:只有子函数不能使用到父函数的内部变量。才能被优化。因此闭包无法优化
。
来源:oschina
链接:https://my.oschina.net/u/4162046/blog/3213718