闭包

作者: 嘤嘤嘤998 | 来源:发表于2019-02-12 12:32 被阅读0次
      function f1(){
        n=999;
        function f2(){
          alert(n); // 999
        }
      }
    

    既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

      function f1(){
        n=999;
        function f2(){
          alert(n);
        }
        return f2;
      }
      var result=f1();
      result(); // 999
    

    闭包就是能够读取其他函数内部变量的函数。

    在JS中,作用域里没有以花括号包围的“块级作用域”概念,看一个例子:

    if(true){
        var a = 1;
    }
    console.log(a);  //输出1
    

    表面上看来, a在if语句里面定义着,那么console语句应该是访问不了的,然而事实恰恰相反,因为在JS中,if或者for语句的花括号,根本不是独立作用域,只有函数的花括号才起作用。

    创建一个匿名函数并立刻执行

    理论上讲,创建一个匿名函数并立刻执行可以这么写:

    function (x) { return x * x } (3);
    

    但是由于JavaScript语法解析的问题,会报SyntaxError错误,因此需要用括号把整个函数定义括起来:

    (function (x) { return x * x }) (3);
    
    变量的寿命兼闭包的第一大作用:延长寿命

    不使用闭包:

    var func = function(){
          var a = 1;  //退出后函数局部变量a直接被销毁
          a++;
          console.log(a);
    }; 
    func();  //2
    func();  //2
    func();  //依然是2
    

    使用闭包:

    var func = function(){
          var a = 1;
          return function(){  //匿名函数
                a++;
                console.log(a);
          }
    };
    var f = func(); //f是对func()的引用
    f();   //输出2
    f();   //输出3
    f();   //输出4
    
    闭包的第二大作用:封闭变量

    测试这段代码,就会发现无论点击那个div,最后弹出的结果都是5.这是因为div节点的onclick事件是被异步触发的,当事件被触发的时候,for循环早已经结束,所以i变量的值已经是5

    <html>
        <body>
            <div>1</div>
            <div>2</div>
            <div>3</div>
            <div>4</div>
            <div>5</div>
        <script>
            var nodes = document.getElementsByTagName('div');
            for(var i = 0, len = nodes.length; i < len; i++){
                nodes[i].onclick = function(){
                    alert(i);
                }
            };
        </script>
      </body>
    </html>
    

    闭包解决:将每次循环的i值封闭起来, 当沿着作用域链从内到外查找变量i时,会先找到被封闭在闭包环境中的i

    for(var i = 0, len = nodes.length; i < len; i++){
        (function(i){       
            nodes[i].onclick = function(){
                    console.log(i);
                }
            })(i)
    };
    
    第三大作用:模拟面向对象

    来看下面用面向对象实现的代码:

    var extent = {
        value:0,
        call:function(){
            this.value++;
            console.log(this.value);
        }
    };
    extent.call();  //输出:1
    extent.call();  //输出:2
    extent.call();  //输出:3
    

    或者:

    var Extent = function(){
        this.value = 0;
    };
    Extent.prototype.call = function(){
        this.value++;
        console.log(this.value);
    };
    var extent = new Extent();
    extent.call(); //输出:1
    extent.call(); //输出:2
    extent.call(); //输出:3
    

    如果用闭包的方法,该怎么写呢?

    var extent = function(){
        var value = 0;
        return {
            call:function(){
                value++;
                console.log(value);
            }
        }
    };
    var extent = extent();
    extent.call();  //输出:1
    extent.call();  //输出:2
    extent.call();  //输出:3
    

    闭包还可以把多参数的函数变成单参数的函数。例如,要计算xy可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2和pow3:

    'use strict';
    function make_pow(n) {
        return function (x) {
            return Math.pow(x, n);
        }
    }
    // 创建两个新函数:
    var pow2 = make_pow(2);
    var pow3 = make_pow(3);
    
    console.log(pow2(5)); // 25
    console.log(pow3(7)); // 343
    
    使用闭包的注意点

    1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

    2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便

    相关文章

      网友评论

          本文标题:闭包

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