闭包和递归

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

你可能感兴趣的:(前端javascript)