闭包函数

1.闭包函数
闭包是一个函数
2.闭包作用
函数外面访问函数内部变量(局部变量)
3.闭包语法
闭包语法有很多种写法,但是一般分为三个环节
1)在外部函数中 声明一个闭包函数 (闭包函数可以访问1级链中的局部变量)
2)在闭包函数中,返回你想要访问的局部变量
3)在外部函数中,返回这个闭包函数
闭包本质:沟通 全局作用域 与 局部作用域 的一座桥梁

闭包作用引入:
1)js作用域
全局作用域(全局变量) 生命周期 : 从页面加载开启, 到页面关闭结束
局部作用域(局部变量) 生命周期 : 从执行函数开始, 到函数执行完毕结束

 //1. 需求 : 在函数外面访问函数内部变量
   function outer() {
            var person = {
                name: 'kunkun'
            }
     }
   outer(); // 调用函数:执行函数体
   // console.log(person); // 报错 person is not defined
   // 报错原因: 局部变量声明周期 是从执行函数开始 到执行函数结束
  // 生命周期:一个变量从开辟内存,到被系统回收的过程

2)返回值:return 值
弊端: 每一次返回的数据,不是同一个 (有一定的内存资源浪费)

 function outer() {
        var person = {
            name: 'kunkun'
        }
        return person;
    }
    var p1 = outer();
    console.log(p1);

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

    //思考 : p1 和 p2 虽然存储数据一样,但是是同一个对象吗?
    // 不是同一个 : outer函数每调用一次,就会在堆中开辟空间, 虽然数据一样但是两个地址不同
    console.log(p1 == p2); //false

3)使用闭包

 function outer() {
     // 1级链
    var person = {
        name: 'kunkun'
    }
    // 1. 在外部函数中声明一个内部函数(闭包函数)
    function closure() {
        // 2.在闭包函数中,返回想要获取的局部变量
        return person;
    }
    // 3.在外部函数中,返回这个闭包函数
    return closure;
}

// 调用外部函数:返回闭包函数
var bibao = outer();

// 掉用闭包函数:得到person
var p1 = bibao();
console.log(p1);
var p2 = bibao();
console.log(p2);

// p1和p2是同一个对象吗? 是
console.log(p1 == p2); //true

4.闭包注意点
得到 相同的值:外部函数(outer)调用一次,闭包函数(closure)调用多次
得到 不同的值:外部函数调用多次,闭包函数调用一次

   function outer() {
        var num = Math.ceil(Math.random() * 100);
        // (1)声明一个闭包函数
        function closure() {
            // (2)返回你想要访问的局部变量
            return num;
        }
        return closure;
    }
   // 1. 得到的是 同一个变量:外部函数调用依次,闭包函数调用多次
    // 外部函数调用依次
    var bibao = outer();
    // 闭包函数调用多次
    var num1 = bibao();
    var num2 = bibao();
    var num3 = bibao();
    console.log(num1, num2, num3); //同一个num
  // 2. 得到的是 不同的变量: 外部函数调用多次,闭包函数调用依次
    // 外部函数调用多次
    /* 
     var bibao1 = outer();
     var n1 = bibao1();
     var bibao2 = outer();
     var n2 = bibao2();
     var bibao3 = outer();
     var n3 = bibao3();
      */

    // 函数连续调用
    var n1 = outer()();
    var n2 = outer()();
    var n3 = outer()();
    console.log(n1, n2, n3); // 不同的数字
  // 3. 测试题
   // 有一个投票机,作用 投票
    function toupiao() {
        var num = 10;

        function closure() {
            num++;
            return num;
        }
        return closure;
    }
    // 同一个投票机,连续投三票
    // 相同的值:外部函数调用一次,闭包函数调用多次
    var bibao = toupiao();
    var p1 = bibao();
    var p2 = bibao();
    var p3 = bibao();
    console.log(p1, p2, p3); // 11 12 13

    // 三个投票机,各投一票
    // 不同的值,外部函数调用多次,闭包函数调用一次
    var piao1 = toupiao()();
    var piao2 = toupiao()();
    var piao3 = toupiao()();
    console.log(piao1, piao2, piao3); // 11 11 11

5.闭包练习与应用场景
1)点击显示按钮索引

<body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>

    <script>
        var buttonList = document.querySelectorAll('button');
        // 遍历按钮数组,注册点击事件
        for (var i = 0; i < buttonList.length; i++) {
            // 声明外部函数
            function outer() {
                // 声明局部变量
                var num = i;
                // 1.声明闭包函数
                function closure() {
                    // 2. 访问局部变量
                    console.log(num);
                }
                // 2. 返回闭包函数
                return closure;
            }
            // 循环每走一次,就会调用一次外部函数,得到一个闭包函数
            var bibao = outer();
            // 事件处理函数,注册不会执行,而是点击执行
            buttonList[i].onclick = bibao;
        }


        // 高级简洁写法
        for (var i = 0; i < buttonList.length; i++) {
            buttonList[i].onclick = function (num) {
                return function () {
                    console.log(num);
                };
            }(i);
        }
    </script>
</body>

2)循环中的定时器(面试题)
面试题:下面代码打印是 1 2 3吗,如果不是,那么请问怎么才能打印 1 2 3
分析: 打印 4 4 4
原理:定时器类似于事件.注册的时候函数不会执行的,而是等页面加载完毕之后过一会执行.与事件区别只是时机不同
此时循环早已结束,全局变量i为固定值4

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


使用闭包解决
  for (var i = 1; i <= 3; i++) {
            // 声明外部函数
            function outer() {
                var num = i;
                // 使用闭包
                function closure() {
                    console.log(num);
                }
                return closure;
            }
            // 调用外部函数,得到一个闭包函数(调用三次外部函数,得到三个不同闭包 1 2 3 )
            var bibao = outer();
            setTimeout(bibao, 1000);
        }



        // 高级精简写法
        for (var i = 1; i <= 3; i++) {
            setTimeout(function (num) {
                return function () {
                    console.log(num);
                }
            }(i));
        };

3)斐波那契数列
三种方式:
递归 : 性能最低,浪费CPU (存在重复计算)
数组+循环 : 性能其次,浪费内存 (数组会存储很多元素,求100列数组就会存100个元素)
闭包+递推法 :性能最好 (没有重复计算,也不会浪费内存)

function feiBo() {
        var arr = [1, 1, 1];

        //1.声明闭包函数
        function closure(n) {
            //每一次计算 : 数组复位
            arr = [1,1,1];

            for (i = 2; i < n; i++) {
                //计算
                arr[2] = arr[1] + arr[0];
                //结果前移,用于下一次计算
                arr[0] = arr[1];
                arr[1] = arr[2];
            };

            //2.返回访问的局部变量
            return arr[2];
        };

        //3.返回闭包函数
        return closure;


    };

    //外部函数调用一次得到闭包函数
    var bibao = feiBo();

    //闭包函数调用多次
    console.log( bibao(10) );
    console.log( bibao(15) );
    console.log( bibao(20) );

4)闭包经典面试题

 1.
       var name = 'The Window';
        var object = {
            name: 'My Object',
            getNameFunc: function () {
                return function () { return this.name }
            }
        };
     console.log(object.getNameFunc()()); //"the window"
 
  //(1)  object.getNameFunc() : 返回一个全局的匿名函数  
        //(2)  调用这个匿名函数 : 此时this指向window
        // var fn = object.getNameFunc();//全局函数
        // fn();// this:window
2.
     var name = 'The Window';
        var object = {
            name: 'My Object',
            getNameFunc: function () {
                // this : object  声明局部变量存储1级链的this :object
                var that = this;
                return function () { 
                    // 返回局部变量that的值
                    return that.name 
                }
            }
        };
         console.log(object.getNameFunc()());//'myobject'
         
 //(1) object.getNameFunc() : 调用对象的方法 , 返回全局匿名函数
        //(2) 调用匿名函数 : 此时匿名函数中的this还是window,但是这个函数返回的不是this,而是that

5)闭包经典应用场景:沙箱模式
沙箱模式(js设计模式之一): 是一个封闭的内存空间(局部作用域), 通常是一个匿名函数自调用
.沙箱模式作用 :
封闭的内存空间 : 不存在全局变量污染
模块化开发 : 一个功能对应一个作用域
访问沙箱的成员(暴露接口) : 把沙箱内部的成员,赋值给window属性

 (function (w) {
            //局部作用域
            var obj = {
                name: '张三'
            };
            
            //封装 : 将函数放入对象的方法中
            obj.eat = function(){};
            obj.sayHi = function(){};

            /*如何在全局作用域  访问 沙箱内部的成员(局部变量) : 闭包场景 

            注意点: 千万不要在沙箱内部访问全局变量
                a. 破坏封装性 :  好不容易封起来了,你又用全局的
                b. 避免代码压缩错误 : (后面代码会压缩成一行,去掉空格,复杂英文变成简写)
                c. 解决方案: 函数传参,把全局变量作为参数传递
             */
            //暴露接口 : 让外部可以访问沙箱里面的成员
            w.obj = obj;

        })(window);
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值