递归,作用域,闭包,缓存,沙箱

使用递归获取后代元素
   1.自己调用自己
   2.要有结束的条件
 //斐波那契 数列
        //1 1 2 3 5 8 13 21
        //1 1
        //2 1
        //3 第1项 + 第2项
        //4 第2项 + 第3项
        //5 第3项 + 第4项

        //n (n-2)+(n-1)

        function fibonacci(n){
            if(n <= 2){
                return 1;
            }
            return fibonacci(n-1) + fibonacci(n-2);
        }

        console.log(fibonacci(10));
2.作用域
 什么是作用域
    变量起作用的范围
### 什么是块级作用域
    JS中没有块级作用域,使用代码块限定的作用域就是块级作用域
### JS中的作用域叫做 词法作用域
### 词法作用域
    在代码写好的时候,就能确定变量的作用域,这种作用域就是词法作用域
    动态作用域(和词法作用域相对).(是词法作用域就不可能是动态作用域)
    在js当中,只有函数能创造作用域
    var num = 123;
    function f1(){
        console.log(num);  //如果是动态作用域打印的就是456 如果是词法作用域 打印123
    }
    function f2(){
        var num = 456;
        f1();
    }
    f2();

### 变量提升

    JS代码的运行分两个阶段
    * 预解析阶段
        * 变量名和函数提升
            将var声明的变量名和function开头的函数进行提升
            提升到当前作用域的最上方
    * 执行阶段
    注意:
        1.变量和函数同名的时候
            只提升函数,忽略变量名
        2.函数同名的时候
            都提升,但是后面的函数会覆盖前面的函数
        3.函数表达式,只会提升变量名,不会提后面的函数
        func();
        var func = function(){
            alert("你猜我会不会被调用");
        }

        //提升后的代码
        var func;
        func();
        func = function(){
            alert("你猜我会不会被调用");
        };
        4.变量提升只会将变量和函数提升到当前作用域的最上方
        ```js
         funciton foo(){
            var num =123;
         }
        ```
        5.变量提升是分块 <script> 的
<script>
foo()
function foo(){
       console.log("第一个script标签内的函数")
 };
</script>

<script>
foo()
function foo(){
   console.log("第2个script标签内的函数")
}
</script>            
        6.条件式函数声明 能否被提升,取决于浏览器, 不推荐使用!!!
        ```
        foo();//会报错,因为未被提升
        if(true){
            function foo(){
            }
        }

        ```
## 作用域链
    只要是函数都有作用域,函数内部的作用域可以访问函数外部的作用域
    当多个函数嵌套的时候,就会形成一个链式的结构,这个就是作用域链
## 绘制作用域链图的步骤
    1.先绘制0级作用域链
    2.在全局作用域中查找,变量和函数的声明,找到之后,将所有的变量和函数用小方格放在0级作用域链上
    3.再从0级作用域链上的函数引出1级作用域链
    4.再去每一个1级作用域链中查找变量和函数的声明,找到之后.....
    5.以此重复,就画好了整个作用域链
        //f1--->全局
        function f1(){
            //f2--->f1--->全局
            function f2(){
                //f3---->f2--->f1--->全局
                function f3(){
                }
                //f4--->f2--->f1---->全局
                function f4(){
                }
            }
            //f5--->f1---->全局
            function f5(){
            }
        }

注意:

       1 设置值的时候,也是访问变量
        2. 获取值的时候,是访问变量
        3.并不是在函数内部写了变量,这个变量就属于这个函数的作用域,而是必须使用var来声明变量,这个变量才会属于这个作用域

## 变量的搜索规则
    1.首先在访问变量的作用域中查找该变量,如果找到直接使用
    2.如果没有找到,去上一级作用域中继续查找,如果如果找到直接使用
    3.如果没有找到,继续去上一级作用域中继续查找,直到全局作用域
    4.如果找到了就用,如果没有直到就报错
 
## 闭包
### 闭包是什么
    一个封闭的对外不公开的包裹结构或空间
### js中的闭包是函数
### 闭包要解决的问题
    1、在函数外部访问不到函数内部的数据
    2、要解决的问题就是需要在外部间接的访问函数内部的数据
 
###闭包的原理就是作用域访问原则
        上级作用域无法直接访问下级作用域中的变量
### 闭包的基本结构
        闭包基本模式
        //在外部函数(foo)内创建函数(inner),在这个内部函数(inner)中,可以操作foo中的数据
        //将外部函数的返回值设置为内部函数
        //在外部调用外部函数(foo),就可以接受到返回值(内部函数)
        //使用这个内部函数,就可以在外部对外部函数里的变量进行修改
    function outer(){
        var data = "数据";
        return function(){
            return data;
        }
    }

    function outer(){
        var data = "数据";
        var data1 = "数据1";
        return {
            getData:function(){
                return data;
            },
            getData1:function(){
                return data1;
            }
        }
    }
   
     function outer(){
            var data = "数据";
            return {
                getData:function(){
                    return data;
                },
                setData:function(value){
                    data = value;
                    return data;
                }
            }
        }

## 闭包的作用

最基本的作用:可以通过闭包返回的函数或者方法,来修改函数内部的数据

如果把数据放在全局作用域内,那么所有人都可以随意修改,这个数据就不再可靠。
闭包可以创建一个私有空间,在这个空间内部的数据,外部无法直接访问
外部空间想要访问函数内部的数据,只能通过闭包提供的指定的方法,在这个方法内部可以设置一些校验规则,让数据变得更加的安全。
如:
function foo(){
            var name = "潘明";
            var badLevel = -1000000000000000000000000000000000;
            return {
                getName: function () {
                    return name;
                },
                setName: function (value) {
                    name = value;
                    return name;
                },
                getBadLevel:function(){
                    return badLevel;
                },
                setBadLevel:function (value) {
                    //在函数外部想要修改数据
                    //只能通过函数内部的方法
                    //我们可以在函数内部定义的这个方法里
                    //设置安全措施,校验之类的操作
                    //可以保证系统的安全性和稳定性
                    if(value > 0 ){
                        throw "你敢说我坏!!!";
                    }

                    badLevel = value;
                    return badLevel;
                }
            }
        }

//

        var obj = foo();
//        obj.setName("王二");
        obj.setBadLevel(obj.getBadLevel() * -1 * Math.pow(10,10000000));
        console.log(obj.getBadLevel());

   
       
 
使用闭包获取多个变量
 function foo() {
            var name = "张国荣";
            var age = 18;

            return {
                getName:function () {
                    return name;
                },
                getAge:function () {
                    return age;
                }
            }
        }

        var obj = foo();
        console.log(obj.getName());
        console.log(obj.getAge());
## 闭包练习
    setTimeout的执行时机
        所有的主任务的代码执行完毕之后,去检查所有的setTimeout回调函数,如果到时间了就执行
    用闭包来解决回调函数在调用的时候访问的是全局的变量
    在闭包中创建一个变量,来单独存储当前的回调函数需要的数据,
    在调用的时候就会去使用这个单独的数据,而不是去访问全局变量
    注册点击事件的时候
    点击事件在触发的时候访问的是全局的变量
    在闭包中创建一个变量,来单独存储当前的事件处理函数需要的数据,
    在调用的时候就会去使用这个单独的数据,而不是去访问全局变量

## 闭包缓存
    缓存(cache):将常用的数据进行存储,以提升性能
    硬件缓存
    浏览器缓存
    CDN(Content Delivery Network)
    内存型数据库(非关系型)MongoDB Redis
网站静态页面缓存机制
      将网页静态化,存储在服务器端
    如何用闭包实现缓存
    1、写一个闭包在闭包中创建一个对象,用来做缓存的存储对象
    2、在闭包中创建一个对象,用来做缓存的存储对象
    3、在闭包中创建一个数组,用来存储换中的键
    4、返回一个函数,这个函数需要两个参数,一个是key 一个是value
    5、在返回的函数中,判断传入的value是否为undefined
    6、如果为Undefined 则表示是获取值,就直接返回在第一步创建的缓存对象中指定的键对应的值
    7、如果不为Undefined 则表示是设置值
    8、在缓存对象中设置指定的key的值为value
    9、把key加入存储key的数组
    10、判断key数组是不是超出了缓存大小限制
    11、如果超出限制,删除数组第一个元素(使用shift),获取到删除的key
    12、使用删除的key删除缓存对象中存储的值(delete)
## 使用缓存解决斐波那契数列的性能问题
    就是将已经计算过的数字缓存进一个数组中,下次再来访问的时候,直接在数组中进行查找,如果找到直接使用,如果没有找到,计算后将数字存入数组,然后返回该数字
解决方案
基本方案:      
       var count =0 ;//计算计算次数
        function createFib(){
            var cache = [];
            function fib(n){
                count ++;
                //1.从cache中获取数据
                if(cache[n] !== undefined){
                    //如果缓存中有 直接返回
                    return cache[n];
                }
                //如果缓存中没有 就计算
                if(n <= 2){
                    //把计算结果存入数组
                    cache[n] = 1;
                    return 1;
                }
                var temp = fib(n - 1) + fib(n - 2);
                //把计算结果存入数组
                cache[n] = temp;
                return temp;
            }
            return fib;
        }


//升级版:
//        创建缓存容器

        function createCache(){
            var cache = {};
            return function (key, value) {
                //如果传了值,就说明是设置值
                if(value !== undefined){
                    cache[key] = value;
                    return cache[key];
                }
                //如果没有传值,只穿了键,那就是获取值
                else{
                    return cache[key];
                }
            }
        }

        var count =0 ;
        function createFib(){
            var fibCache = createCache();
            function fib(n){
                count ++;
                //1.从cache中获取数据
                if(fibCache(n) !== undefined){
                    //如果缓存中有 直接返回
                    return fibCache(n) ;
                }
                //如果缓存中没有 就计算
                if(n <= 2){
                    //把计算结果存入数组
                    fibCache(n , 1) ;
                    return 1;
                }
                var temp = fib(n - 1) + fib(n - 2);
                //把计算结果存入数组
                fibCache(n, temp) ;
                return temp;
            }

            return fib;
        }


        var fib = createFib();
//        console.log(fib(6));
        fib(5);
        console.log(count);
        count = 0;
        fib(6);
        console.log(count);
        count = 0;
        fib(20);
        console.log(count);
        count = 0;
        fib(21);
        console.log(count);
        count = 0;

缓存:

jQuery 实现缓存      
         function createCache(){
            //cache对象中以键值对的形式存储我们的缓存数据
            var cache = {};
            //index数组中该存储键,这个键是有顺序,可以方便我们做超出容量的处理
            var index = [];
            return function (key, value) {
                //如果传了值,就说名是设置值
                if(value !== undefined){
                    //将数据存入cache对象,做缓存
                    cache[key] = value;
                    //将键存入index数组中,以和cache中的数据进行对应
                    index.push(key);

                    //判断缓存中的数据数量是不是超出了限制
                    if(index.length >= 50){
                        //如果超出了限制
                        //删除掉最早存储缓存的数据
                        //最早存入缓存的数据的键是在index数组的第一位
                        //使用数组的shift方法可以获取并删除掉数组的第一个元素
                        var tempKey = index.shift();
                        //获取到最早加入缓存的这个数据的键,可以使用它将数据从缓存各种删除
                        delete cache[tempKey];
                    }
                }
                //如果没有传值,只穿了键,那就是获取值
//                else{
//                    return cache[key];
//                }
                return cache[key];
            }
        }

## 沙箱模式
    沙箱模式就是一个封闭的独立的环境,外界无法修改该环境内任何信息,沙箱内的东西单独属于一个世界
      360沙箱模式
      将软件和操作系统进行隔离,以达到安全的目的
      苹果手机的app使用的就是沙箱模式去运行
       隔离app的空间,每个app独立运行
 
    沙箱模式的基本模型
    (function(){
        //变量定义
        //逻辑代码
        //如果需要,向window对象添加成员,以暴露接口
    })()   //IIFE
为什么要使用立即执行函数表达式(匿名函数)(IIFE)?
因为IIFE不会再外界暴露任何的全局变量,但是又可以形成一个封闭的空间,刚好可以实现沙箱模式
//jQuery当中的沙箱模式
        (function(win){

            var itcast = {
                getEle:function () {

                }
            }

            //如果需要在外界暴露一些属性或者方法,就可以将这些属性和方法
            //加到window全局对象上去
            //但是这window全局对象不可以直接引用,因为直接引用会破坏沙箱原则
            //所以我们选择使用传参的形式将 window对象 传入沙箱内
            //此时沙箱内使用window对象的时候,不会再去全局搜索window对象
            //而使用的就是沙箱内部定义的形参

            win.itCast = win.$ = itcast;

        })(window)

       //沙箱模式一般应用在书写第三方框架
        //或者为第三方框架书写插件
        //或者书写功能独立的一些组件

        //沙箱模式的优势
        //1.沙箱模式使用的是IIFE,不会再外界暴露任何的全局变量,也就不会造成全局变量污染
        //2.沙箱中的所有数据,都是和外界完全隔离的,外界无法对其进行修改,也就保证了代码的安全性


        //js中沙箱模式的实现原理就是
        //函数可以构建作用域!上级作用域不能直接访问下级作用域中的数据

  
 
原文地址:https://www.cnblogs.com/aimeeblogs/p/9520645.html