首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端day20-JS高级(递归和闭包)学习笔记

前端day20-JS高级(递归和闭包)学习笔记

原创
作者头像
帅的一麻皮
修改2020-05-11 10:54:23
1.2K0
修改2020-05-11 10:54:23
举报
文章被收录于专栏:前端与Java学习前端与Java学习

01-递归

1.1-递归函数介绍

  • 1.递归函数:一个函数自己调用自己
  • 2.递归函数特点
    • a.一定要有结束条件,否则会导致死循环
    • b.能用递归函数实现的需求,就一定可以用循环调用函数来解决,只是代码简洁与性能不同而已
      • 递归会影响性能,每一次递归都会进栈容易造成栈溢出,而循环不会

1.2-递归应用场景1(累加和,阶乘)

<script>
        //1.求1-n之间的累加和
        function getSum(n){
            //递归 : 自己调用自己
            return n == 1? 1 : getSum(n-1) + n;
            // if(n == 1){
            //     return 1;
            // }else{
            //     return getSum(n-1) + n;
            // };
        };
        var num1 = getSum(100);
        console.log(num1);
        

        //2.求阶乘
        var res = (function(n){return n==1?1:n*arguments.callee(n-1)})(6);
        console.log(res);

        function getJieChen(n){
            //递归实现
            return n == 1?1:n*getJieChen(n-1);

            // if(n == 1){
            //     return 1;
            // }else{
            //     return n * getJieChen(n-1);
            // };

            // if(n == 1){
            //     return 1;
            // }else if(n == 2){
            //     return 2 * getJieChen(1)
            // }else if(n == 3){
            //     return 3 * getJieChen(2)
            // }
            // ………………
            // else if(n == n){
            //     return n * getJieChen(n-1)
            // };

            //使用循环思路
            // var sum = 1;
            // for(var i = 1;i<=n;i++){
            //     sum *= i;
            // };
            // return sum;
        };
        var n1 = getJieChen(5);
        console.log(n1);
        

        //3.求斐波那契额数列
        //1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597
        function getFeiBo(n){
            //使用递归实现
            // if(n == 1 || n == 2){
            //     return 1;
            // }else{
            //     return getFeiBo(n-1) + getFeiBo(n-2);
            // };

            // if(n == 1 || n == 2){
            //     return 1;
            // }else if(n == 3){
            //     return getFeiBo(2) + getFeiBo(1)
            // }else if(n == 4){
            //     return getFeiBo(3) + getFeiBo(2)
            // }
            // ……………………
            // else if(n == n){
            //     return getFeiBo(n-1) + getFeiBo(n-2);
            // };

            //数组+循环实现
            var arr = [1,1];
            for(var i = 2;i<n;i++){
                arr[i] = arr[i-1] + arr[i-2];
            };
            console.log(arr);
            
            return arr[arr.length-1];
        };

        var res = getFeiBo(2000);
        console.log(res);
    </script>

1.3-递归应用场景2(遍历DOM树)

<div id="box">一级
        <div>1
            <div>11
                <p>111
                    <b>1111</b>
                    <b>2222</b>
                </p>
                <p>112
                        <b>1112</b>
                        <b>111222</b>
                    </p>
            </div>
        </div>
        <div>2
            <p>我是pp
                <b>我是bb</b>
            </p>
        </div>
        <p>3
            <p>我爱你</p>
            <p>一万年
                <b>这不是真的</b>
            </p>
        </p>
    </div>
    <script>
        /* 
        遍历DOM树 :获取某个元素所有的后代元素
        1.以前学的的webapi,有没有直接的方法可以获取呢?
            没有。 元素.children 只能获取子元素

        2.写一个函数: 获取某个元素的子元素,子元素又有子元素,又可以继续调用这个函数
        获取子元素的子元素,以此类推形成递归调用
         */

        function getHouDai(ele){
            for(var i = 0;i<ele.children.length;i++){
                arr.push(ele.children[i]);
                //ele的子元素也有可能有自己的子元素,继续调用函数获取子元素的子元素
                getHouDai(ele.children[i]);
            };
        };

        // var arr = [];//声明数组存储所有的后代元素
        // var box = document.getElementById('box');
        // getHouDai(box);
        // console.log(arr);

        var arr = [];
        getHouDai(document);//遍历文档树
        console.log(arr);
    
    </script>

02-闭包(难点与重点)

2.1-闭包介绍
  • 1.闭包(closurc)作用:在函数外部访问函数内部变量
    • 闭包一个可以获取其他函数内部变量的函数
  • 2.语法
    • a.外部函数内部声明一个闭包函数
    • b.在闭包函数中返回想要访问的局部变量
    • c.外部函数中返回这个闭包函数
  • 3.本质
    • 函数内部与函数外部连接起来的一座桥梁

  • 为什么要学习闭包
    • 1.需求引入:想要在函数外部访问函数内部的变量
    • 2.思考能不能直接获取:不能
      • 原因:函数执行完毕之后局部变量会被系统回收
      • 复习js作用域
        • 全局作用域(全局变量):函数外面声明的变量,可以在任何地方访问
        • 局部作用域(局部变量):函数里面声明的变量,只能在函数里面访问
    • 3.继续思考,使用return返回这个变量
      • 无法实现需求
        • 因为每一次调用函数都会重新声明一个新的变量,并不是同一个
    • 4.闭包:可以保证函数内部变量只会声明一次,多次访问都是同一个变量

问题:如何在函数外部访问函数内部的变量?

1.直接获取

        //1.直接在外部是获取不到函数内部的变量的
        function fn(){
            var person = {
                name:"老八",
                age:28
            }
        }
        fn();
        //函数外部无法直接获取函数内部的变量
        console.log(person);//报错person is not defined    
        

2.通过函数返回值return

        //2.使用函数返回值:return(有一定的内存资源浪费)
        // 两次获取的不是同一个对象,无法实现需求
        function fn() {
            var person = {
                name: "老八",
                age: 28
            }
            return person;
        }
        var p1 = fn();
        console.log(p1);
        var p2 = fn();
        console.log(p2);
        //这两次获取的person是同一个吗?
        //不是同一个,fn函数每调用一次,就会在堆中开辟空间,虽然数据一样但是两个地址不同
        console.log(p1 == p2); //false    

3.使用闭包

        /* 
            1.闭包是什么:闭包是一个函数
            2.闭包的作用:函数外面 访问函数内部变量(局部变量)
            3.闭包语法:闭包语法有很多种写法,但是一般分为三个环节
                a.在外部函数中 声明一个闭包函数(闭包函数可以访问1级链中的局部变量)
                b.在闭包函数中 返回你想要访问的局部变量
                c.在外部函数中 返回这个闭包函数

            闭包函数:沟通全局作用域与局部作用域的一座桥梁
         */
        function fn() {
            var person = {
                name: "老八",
                age: 28
            }
            //1.在外部函数中声明一个内部函数(闭包函数)
            function closure(){
                //2.在闭包函数中 返回你想要获取的局部变量
                return person;
            }
            //3.在外部函数中 返回这个闭包函数
            return closure;
        }
        //调用外部函数fn:返回闭包函数closure的地址被bibao变量接受
        var bibao1 = fn();
        // var bibao2 = fn();

        var p1 = bibao1();
        console.log(p1);

        var p2 = bibao1();
        console.log(p2);

        console.log(p1 == p2); //true    

2.2-闭包语法注意点

  • 1.复习闭包标准语法
  • 2.如果希望访问函数内部变量是同一个,外部函数只能调用一次
  • 3.如果外部函数调用多次,则每一次都会重新声明内部变量
<script>
        /* 
        1.闭包语法
            a.在外部函数中 : 声明闭包函数
            b.在闭包函数中: 返回你想要访问的变量
            c.在外部函数中 : 返回闭包函数
        2.闭包语法注意点
            如果希望得到同一个变量 : 外部函数只调用一次,闭包函数可以调用多次
            如果希望得到不同变量 : 外部函数调用多次,闭包函数只调用一次
         */

        function outer(){
            var num = Math.ceil(Math.random()*100);
            //1.声明闭包函数
            function closure(){
                //2.返回你想要访问的变量
                return num;
            };
            //3.返回这个闭包函数
            return closure;
        };

        //(1)得到的是同一个数据 :  外部函数只调用一次,闭包函数调用多次
        var bibao =  outer();
        var num1 = bibao();
        var num2 = bibao();
        var num3 = bibao();
        console.log(num1,num2,num3);

        //(2)得到的是不同数据 : 外部函数调用多次,闭包函数调用一次
        // var bibao1 =  outer();
        // var n1 =  bibao1();

        var n1 =  outer()();
        // var bibao2 =  outer();
        // var n2 =  bibao2();
        var n2 = outer()()
        // var bibao3 =  outer();
        // var n3 =  bibao3();
        var n3 = outer()();

        console.log(n1,n2,n3);

了解了闭包之后做一道测试题:

for (var i =1;i<=3;i++){
        function outer (  ) {
            var num = i;
            function closure (  ) {
                return num;
            };
            return closure;
        };
        console.log ('循环内:' +  outer () () );//会打印什么
    };

    console.log ('循环外' + outer () () )//会打印什么

2.3-闭包语法练习题

1-点击显示li元素索引

    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    <script>
        //需求:点击显示按钮索引

        //获取按钮
        var btnList= document.querySelectorAll("button");
        // for(var i=0;i<btnList.length;i++){
        //     function outer(){
        //         var num =i;
        //         //1 声明闭包函数
        //         function closure(){
        //             console.log(num);
        //         }
        //         return closure;
        //     }
        //     var bibao = outer();
        //     btnList[i].onclick = bibao;
        // }

        //高级写法
        for(var i=0;i<btnList.length;i++){
            (function(i){
                btnList[i].onclick = function(){
                    console.log(i);
                }
            })(i)
        }
        
    </script>    

2-循环中的定时器

<script>
        //循环中的定时器

        //需求: 开启三个定时器,每隔1s分别打印1 2 3

        for (var i = 1; i <= 3; i++) {
            setTimeout(function () {
                console.log(i);
            }, 1000);
        };

        //使用循环解决代码冗余问题
        /* 
        定时器中的函数类似于事件处理函数,不会立即执行。而是等页面加载完毕,
        等待1s后执行。此时循环早已执行结束,i全局变量的值变成4
         */
        // for (var i = 1; i <= 3; i++) {
        //     function outer() {
        //         var num = i;
        //         return function() {
        //             setTimeout(function () {
        //                 console.log(num);
        //             }, 1000);
        //         };
        //     }
        //     outer()();
        // };
         
     //简化后
        for (var i = 1; i <= 3; i++) {
            setTimeout(function (num) {
                return function () {
                    console.log(num);
                }
            }(i), 1000);
        };
    </script>

3-闭包实现斐波那契数列

  • 三种实现方式性能排行
    • 1.使用闭包
      • 优点:计算次数少于递归,数组存储元素少于以前做法
    • 2.使用以前的数组方式
      • 弊端:数组中元素越来越多,消耗性能(消耗内存)
      • 优点:代码执行次数较少
    • 3.使用递归函数(性能最低)
      • 弊端:代码执行次数太多,产生重复计算(消耗cpu)
      • 优点:不需要存储大量数据,节省内存
  • 网上有一篇博客详细介绍了斐波那契数列各种算法的性能,经过实际测试,笔者的这种闭包思路性能是最好的(略微高于递推法)
//3.使用闭包:
    //优点:计算次数少于递归,数组存储元素少于以前做法
    /*核心思路
    * 1.数组中只存储三个元素:前两个元素用于存储前两列数字,第三个元素用于占位,存储最后的结果
    * 2.如何避免数组被重复声明:使用闭包延长数组声明周期
     */
   function feiBo() {
        var arr = [1, 1, 0];
        //使用闭包
        //1. 声明闭包函数
        function closure(n){
            for (var i = 2; i < n; i++) {
                arr[2] = arr[0] + arr[1];
                arr[0] = arr[1];
                arr[1] = arr[2];
            };
            console.log(arr);
            //2. 返回想要访问的局部变量
            return arr[2];
        };
        //3.返回闭包函数
        return closure;
    };

    var bibao = feiBo();

    var res =  bibao(15);
    console.log(res);//610

4-闭包经典面试题(来自红皮书-《JavaScript高级程序设计》)

<script>
        //1.
        var name = "The Window";
        var object = {
            name: "My Object",
            getNameFunc: function () {
                return function () {
                    return this.name;
                };
            }
        };
        //object.getNameFunc():返回一个匿名函数,在全局作用域中调用该函数,此时相当于是window调用,所以this指向window
        console.log(object.getNameFunc()()); //"The Window"

        //2.
        var name = "The Window";
        var object = {
            name: "My Object",
            getNameFunc: function () {
                var that = this;
                return function () {
                    return that.name;
                };
            }
        };
        //object.getNameFunc():在getNameFunc函数中,this指向object,使用局部变量that来存储this,所以闭包函数中that指向的是object
        console.log(object.getNameFunc()()); // "My Object"
    </script>

2.4-闭包应用场景:沙箱模式

沙箱:是js一种设计模式,指的是一种封闭的空间,通常是一个自执行函数

  • 作用
    • a.提供不同的作用域 :避免全局变量污染
    • b.模块化开发 :一个可以实现完整功能的独立空间(作用域)
<script>
        /* 
        1.沙箱模式 :是js的一种设计模式(思想),是一个封闭的内存空间.通常是一个匿名函数自调用

        2.沙箱模式好处
            a. 封闭的内存空间(不会存在全局变量污染)
            b. 模块化开发

        3.沙箱模式也是闭包的应用场景
         */

        (function (w){
            //独立作用域
            var person = {};
            person.sayHi = function(){

            };

            person.name = '111';

            person.eat = function(){

            };

            //将局部变量person动态赋值给window的属性
            /*不会在沙箱内部访问全局变量 
            1.破坏封装性
            2.避免代码压缩错误 (以后开发代码会压缩成一行,去掉空格,会复杂英文简写)
             */
             w.person = person;

        })(window);

        console.log(person);//window.person
        
        
    </script>

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01-递归
    • 1.1-递归函数介绍
      • 1.2-递归应用场景1(累加和,阶乘)
        • 1.3-递归应用场景2(遍历DOM树)
        • 02-闭包(难点与重点)
          • 2.1-闭包介绍
            • 2.2-闭包语法注意点
              • 2.3-闭包语法练习题
                • 1-点击显示li元素索引
                • 2-循环中的定时器
                • 3-闭包实现斐波那契数列
                • 4-闭包经典面试题(来自红皮书-《JavaScript高级程序设计》)
              • 2.4-闭包应用场景:沙箱模式
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档