尾调
尾调就是指某个函数的最后一步是返回另一个函数的调用结果。
如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