尾调

陌路散爱 提交于 2020-04-05 19:52:06

尾调

尾调就是指某个函数的最后一步是返回另一个函数的调用结果。

如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的尾调再优化只在严格模式下开启,正常模式下是无效的。 注意:只有子函数不能使用到父函数的内部变量。才能被优化。因此闭包无法优化

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!