美文网首页
递归和闭包

递归和闭包

作者: Fanny | 来源:发表于2020-03-06 14:25 被阅读0次

    js变量的作用域:
    全局作用域(全局变量) : 在函数外面声明的变量
    **生命周期(变量从声明到销毁): 页面从打开到关闭.
    局部作用域(局部变量) : 在函数里面声明的变量
    **生命周: 开始调用函数到函数执行完毕

    1.闭包使用介绍

    1.闭包介绍(closure)
    1.1 闭包 : 是一个可以在函数外部访问函数内部变量的函数
    * 闭包是函数
    1.2 闭包作用: 可以在函数外部访问函数内部变量
    * 延长局部变量的生命周期
    1.3 闭包语法 :
    a. 在外部函数outer的内部声明了一个闭包函数closure
    b. 在闭包函数内部 返回想要访问的局部变量
    c. 在外部函数中返回闭包函数
    1.4 闭包本质:
    是一个沟通函数内部(局部作用域)与函数外部(全局作用域)的桥梁

    //1.需求:在函数外部访问函数内部的变量
            // function fn(){
            //     var zhangsan = {
            //         name:'张三',
            //         age:34
            //     };
            // };
    
            // fn();
            // 函数里面的局部变量在函数之后完毕之后自动被系统回收
            // console.log(zhangsan);
    
     // 2. return返回值
            // 弊端 :浪费内存资源。  每调用一次函数,就生成了一个新的对象
            // function fn(){
            //     var zhangsan = {
            //         name:'张三',
            //         age:34
            //     };
    
            //     return zhangsan;
            // };
    
            // var a = fn();
            // console.log(a);
    
            // var b = fn();
            // console.log(b);
    
            // //a和b是同一个对象吗?
            // //每调用一次函数,就生成了一个新的对象。两个对象虽然数据是一样,但是在堆中两个不同的地址
            // console.log(a == b);
    
    //3.使用闭包实现在函数内部访问函数外部的变量
           //声明外部函数
             function fn(){
                var zhangsan = {
                    name:'张三',
                    age:34
                };
    
                //闭包函数
                function closure(){
                    // console.log(canglaoshi);
                    return zhangsan;
                };
    
                return closure;
                
            };
    
            //调用fn, (1)声明了一个对象张三(0xaaaa)  (2)声明了一个闭包函数closure(0xbbbb)  (3)返回闭包函数
            var bibao = fn();
    
            //调用闭包函数:得到fn中的局部变量
            var a = bibao();
            console.log(a);
            
            var b = bibao();
            console.log(b);
    
            console.log(a == b);//true
    

    2.闭包函数的使用步骤和注意点

    1.复习闭包的语法步骤(3个步骤)
    a.在外部函数outer中声明一个函数closure
    b.在闭包函数中返回想要访问的变量
    c.返回闭包函数
    2.了解闭包语法的注意点
    a. 如果希望访问同一个变量,外部函数只能调用一次
    b. 如果希望访问不同的变量,外部函数可以调用多次
    * 每调用一次生成一个新的局部变量和新的闭包函数

     function outer(){
                var num = Math.floor(Math.random()*100);
                console.log(num);
                //1.声明闭包函数
                function closure(){
                    console.log(num);
                    //2.在闭包函数中返回想要访问的变量
                    return num;
                };
                //3.返回闭包函数
                return closure;
            };
    
    
            //第一个注意点:  如果希望闭包访问的是同一个变量,外部函数只能调用一次
            //1.调用outer : 声明局部变量,声明闭包函数
            //获取闭包函数
            var bibao = outer();
    
            //2.调用闭包函数
            bibao();
            bibao();
            bibao();
    
    
            //第二个注意点: 如果希望闭包访问的是不同的变量,外部函数需要调用多次
            // var bibao1 = outer();
            // bibao1();
            outer()();
    
            //  var bibao2 = outer();
            //  bibao2();
            outer()();
    
            //  var bibao3 = outer();
            //  bibao3();
            outer()();
    
             //2。示例 投票机
    
             function vouter(){
                 var num = 10;
    
                 function closure(){
                     num++;
                     console.log(num);
                     return num;
                 };
                 return closure;
             };
    
             //2.1 一台投票机,投3票
             var bb = vouter();
    
             bb();//11
             bb();//12
             bb();//13
    
             //2.2  三台投票机,各投1票
             var bb1 = vouter();
             bb1();//11
    
            //  var bb2 = vouter();
            //  bb2();//11
            vouter()();
    
            //  var bb3 = vouter();
            //  bb3();//11
            vouter()();
    

    3.由一个练习题看闭包的本质

      //1.
            var name = 'My Window';
            var obj = {
                name:'My Object',
                getFunction:function(){
                    return function(){
                        console.log(this.name);
                    };
                }
            };
    
            //(1) var fn = obj.getFunction()    (2)fn() // window.fn()
            // (1)当调用obj.getFunction()的时候返回一个匿名函数 (返回到全局作用域)
            //(2)调用匿名函数,由于全局的所以函数指向window
            obj.getFunction()();//'My Window';
    
    
            //2.
            var name = 'My Window';
            var obj = {
                name:'My Object',
                getFunction:function(){
                    var that = this;
                    return function(){
                        console.log(that.name);
                    };
                }
            };
    
            //(1)调用obj.getFunction()  : 这个函数中this是obj ,声明了一个局部变量that存储this
            //(2)调用闭包函数,由于闭包函数延长了that的生命周期,所以会打印obj.name属性值
            console.log(obj.getFunction()()); //My Object
    

    4.闭包的经典使用场景

      /* 
            1.沙箱模式:是js的一种设计模式。 一个独立的作用域完成独立的功能,通常是一个匿名函数自调用
            2.沙箱模式好处
                a. 封闭的空间,避免全局变量污染
                b. 模块化开发(一个功能模块对应一个作用域)
             */
             (function(w){
                 var person = {
                     name:'张三',
                     age:18
                 };
                 person.eat = function(){
                     console.log('今天吃米粉');
                 };
                 person.play = function(){
                     console.log('大吉大利,今晚吃鸡');
                 };
                 /* 
                 1.外部如何访问沙箱中的变量?
                 2.使用参数
                    为什么不直接使用window?
                        a.沙箱里面不能直接访问外部变量,破坏封装性
                        b.以后实际开发代码会压缩,可能会把window压缩成w。直接使用无法获取的
                  */
                 w.person = person;
             })(window);
    
             console.log(person);
             person.eat();
    

    递归

    1.递归函数: 函数自己调用自己
    2.递归函数的特点
    a.能用递归实现的功能就一定可以使用循环调用函数来实现,只是语法简洁性与性能不同而已
    b.一定要有结束条件,否则会导致死循环
    注意:递归不可以乱用,因为在有些时候性能不好

    1.递归的简使用

        //递归
            var i = 1;
            function fn(){
                console.log('吾日三省吾身');
                i++;
                if(i<=3){
                    fn();
                }; 
            };
            fn();
    
    
           // 循环
            for(var i = 1;i<=3;i++){
                fn();
            }
            function fn(){
                console.log('吾日三省吾身');
            };
    

    2.递归的使用场景

    //1.求1-n的累加和
    
            //递归实现
            function getSum(n) {
                if (n == 1) {
                    return 1;
                } else {
                    return getSum(n - 1) + n;
                };
                //递归分析
                // if(n == 1){
                //     return 1;
                // }else if(n == 2){
                //     return getSum(n-1) + n;
                // }else if(n == 3){
                //     return getSum(2) + 3;
                // }else if(n == 4){
                //     return getSum(3) + 4;
                // }else if(n == 5){
                //     return getSum(4) + 5;
                // }
                // ***
                // else if(n == n){
                //     return getSum(n-1)  + n
                // }
            };
    
            console.log(getSum(5));//1+ 2 + 3 + 4 + 5 = 15
    
    
     //循环实现
            // function getSum(n){
            //     //求和:箩筐思想三步法
            //     var sum = 0;
            //     for(var i = 1; i<=n;i++){
            //         sum+=i;
            //     };
            //     return sum;
            // };
    
            // console.log(getSum(100));
    
    
     //2.求阶乘(*)
            /*阶乘:
            5! = 5 * 4 * 3 * 2 * 1
            6! = 6 * 5 * 4 * 3 * 2 * 1
             */
    
            //递归实现
            function jieChen(n) {
                return n == 1 ? 1 : jieChen(n - 1) * n;
                //  if(n == 1){
                //      return 1;
                //  }else{
                //      return jieChen(n-1) * n;
                //  };
    
                //递归分析
                // if(n == 1){
                //     return 1;
                // }else if(n == 2){
                //     return jieChen(1) * 2;
                // }else if(n == 3){
                //     return jieChen(2) * 3
                // }else if(n == n){
                //     return jieChen(n-1) * n;
                // }
            };
    
            console.log(jieChen(5));// 5 * 4 * 3 * 2 * 1 = 120
    
            //阶乘奇葩面试题 arguments.callee 得到的是函数本身
            var num = (function (n) { return n == 1 ? 1 : arguments.callee(n - 1) * n })(6);
            console.log(num);
    
    
     //  //循环实现
            //  function jieChen(n){
            //     var sum = 1;
            //     for(var i = 1 ;i<=n;i++){
            //         sum *= i;
            //     };
            //     return sum;
            //  };
    
            //  console.log(jieChen(6));// 5 * 4 * 3 * 2 * 1 = 120
    
    
     //3.斐波那契数列
            //了解: 递归函数虽然语法简洁,但是性能不是一定高于循环
           //检测递归的性能,递归不可以乱用,因为性能不好 
            console.time();
            //递归实现
            function fib(n){
    
                if(n == 1 || n == 2){
                    return 1;
                }else{
                    return fib(n-2) + fib(n-1);
                };
                // if(n == 1 || n == 2){
                //     return 1;
                // }else if(n == 3){
                //     return fib(3-2) + fib(3-1);
                // }else if(n == 4){
                //     return fib(4-1) + fib(4-2)
                // }
            };
    
            console.log(fib(10));
    
            console.timeEnd();
            
    
    
            /* 需求:求斐波那契额数列第十列
    
            1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........
    
            1.前面两个数字固定  1 , 1
            2.第三个数字开始,每一个数字都是前面两个数字的和
             */
    
            function fib(n) {
                var arr = [1, 1];
                for (var i = 2; i < n; i++) {
                    arr[i] = arr[i - 2] + arr[i - 1];
                };
                return arr[arr.length-1];
            };
    
            console.log(fib(100));
            
    
    
              //  arr[2] = arr[2-2] + arr[2-1];
            //  arr[3] = arr[3-2] + arr[3-1];
            //  arr[4] = arr[4-2] + arr[4-1];
    

    3.使用递归遍历dom数

     /*  递归应用场景:遍历DOM树
            
            1.需求:获取father父元素的所有后代元素
            2.DOM的api中有没有直接的方法可以获取呢? 没有
    
            3.递归思路
                a。遍历父元素father
                b. 遍历每一个子元素,找到子元素的子元素。然后又继续遍历子元素的子元素的子元素,以此类推
                形成递归调用
    
             */
    
             var father = document.getElementById('father');
    
             var list = [];//存储所有的后代元素
    
             function houDai(ele){
                 for(var i = 0;i<ele.children.length;i++){
                    list.push(ele.children[i]);
                    //递归求子元素的子元素
                    houDai(ele.children[i]);
                 };
             };
    
             //求父元素的所有后代元素
            //  houDai(father);
            //  console.log(list);
    
            //求整颗dom树
            houDai(document);
            console.log(list);
    

    相关文章

      网友评论

          本文标题:递归和闭包

          本文链接:https://www.haomeiwen.com/subject/erovrhtx.html