zoukankan      html  css  js  c++  java
  • jQuery中的闭包和js中的闭包总结

    关于闭包的知识总结下:

    一、闭包

    1、定义

    闭包的关键是作用域,概念是:能有读取其他函数内部的函数
    使用的场景有很多,最常见的是函数封装的时候,再就是在使用定时器的时候,会经常用到;

    //闭包:有参数的加载事件(空参数形式)
    (function($){
    alert("123");
    })(jQuery);

    //有参数的加载事件
    (function((){ alert());
    })(456);

    (function($){
    $("div p").click(function(){alert("cssrain!")});
    })(jQuery); //一个闭包

    这里面的$只是形参,但jquery是全局变量,所以不需要调用该函数就会自动执行,或者分两步

    就是转化成正常的函数,先写函数,后调用。

    2、实例
    (1)
    function Test()
    {
        var str="Test里面的局部变量";
    
         retrun function()
         {
             alert(str);
          }
        
    }
    
    
     var func=Test();
    
     func();
    //运行后将打印"Test里面的局部变量"
    
    按照变量的生命周期  Test函数运行完毕  str变量就会被销毁  但是在这里它并没有 因为它被匿名函数给捕获了 延长了它的生命周期 这就是闭包
    
    (2)
    (function($){
         $("div p").click(。。。);
    })(jQuery);    
    就是等于
    function tempFunction($){  //创建一个以$为形参的函数
         $("div p").click(....);
    }
    TempFunction(jQuery);         //传入实参jQuery执行函数.
    
     
    
    $(function(cssrain){
         cssrain("div p").click(.... );
    })(jQuery);            //一个闭包
    
    闭包的基本写法:
    $(function(){do someting})();
    //这个你就理解为定义一个匿名函数并立即执行
    带参数的话就这样:
    $(function(形参){do someting})(实参);
    另外
    $(function(){var upc="i am upc"})();
    alert(upc);
    会提示undefined。
    因为闭包后,里面的变量就相当于局部了。
    
    闭包的好处:
    不增加额外的全局变量,
    执行过程中所有变量都是在匿名函数内部。
    
    
    3、闭包的优点和缺点
    (1)、优点:不增加额外的全局变量,

    执行过程中所有变量都是在匿名函数内部。

    (2)、缺点:可能造成内存泄漏。
    关于js闭包这个总结的很好,地址:https://www.cnblogs.com/darrenji/p/3804993.html

    本篇的标题虽然是"jQuery闭包之浅见...",但实际上本篇涉及的多半是javascript"闭包"相关内容,之所以写这个标题,完全是因为自己平常用jQuery写前端习惯了。还有一个原因是:javascript"闭包"很容易造成"内存泄漏", 而jQuery已经自动为我们规避、处理了由"闭包"引发的"内存泄漏"。

    javascript的"闭包"是这样定义的:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

    定义高度提炼,还是从代码实例来体验"闭包"的来龙去脉吧。本篇主要包括:

    • 从面向对象的角度来理解"闭包"
    • 调用内部函数的几种方式
    • 变量的作用域
    • jQuery中的"闭包"
    • 内存泄漏
    从面向对象的角度来理解"闭包"

    在面向对象中,我们是这样定义类:

    public class SomeClass
    {
        private string _someVariable;
     
        public void SomeMethod()
        {
            //TODO:
        }
    }
    

    在javascript中,有一种情况是,一个函数中包含了另外一个内部函数:

    function OutFunction()
    {
        var temp = 0;
        function innerFunction(){
        }
    }
    

    我们把OutFunction称为外部函数,把innerFunction称为内部函数。

    这里的外部函数OutFunction相当于一个类。
    外部函数变量temp相当于类的私有字段。
    内部函数innerFunction相当于类的方法。

    就像我们需要实例化类来调用类的实例方法一样,在javasript中,当我们在外部函数之外调用内部函数的时候,我们把此时的内部函数叫做"闭包",相当于面向对象的实例方法。

    接下来,从"如何调用内部函数"这个角度,来循序渐进地体会何谓"闭包"。

    调用内部函数的几种方式

    在外部函数之外直接调用内部函数

     <script type="text/javascript">
            function outerFunction() {
                console.log('外部函数执行了');
                function innerFunction() {
                    console.log('内部函数执行了');
                }
            }
     
            $(function() {
                innerFunction();
            });
        </script>
    

    结果报错:Uncaught ReferenceError: innerFunction is not defined

    以上情况,内部函数的作用域是在外部函数之内,当在外部函数之外调用内部函数的时候,自然就报错。
    在外部函数之内调用内部函数:

     <script type="text/javascript">
            function outerFunction() {
                console.log('外部函数执行了');
                function innerFunction() {
                    console.log('内部函数执行了');
                }
     
                innerFunction();
            }
     
            $(function() {
                outerFunction();
            });
        </script>
    

    结果:
    外部函数执行了
    内部函数执行了

    以上情况,内部函数的作用域是在外部函数之内,当在外部函数之内调用内部函数,自然不会报错。

    □ 在外部函数之外调用内部函数,把内部函数赋值给一个全局变量

    <script type="text/javascript">
            
            //全局变量
            var globalInner;
     
            function outerFunction() {
                console.log('外部函数执行了');
                function innerFunction() {
                    console.log('内部函数执行了');
                }
     
                globalInner = innerFunction;
            }
     
            $(function() {
                outerFunction();
                console.log('以下是全局变量调用内部方法:');
                globalInner();
            });
        </script>
    

    结果:
    外部函数执行了
    以下是全局变量调用内部方法:
    内部函数执行了

    以上情况,全局变量globalInner相当于面向对象的委托,当把内部函数赋值给全局变量,调用委托方法就会调用内部函数。

    □ 在外部函数之外调用内部函数,把内部函数赋值给一个变量:

     <script type="text/javascript">
     
            function outerFunction() {
                console.log('外部函数执行了');
                function innerFunction() {
                    console.log('内部函数执行了');
                }
     
                return innerFunction;
            }
     
            $(function() {
                console.log('先把外部函数赋值给变量');
                var temp = outerFunction();
                console.log('再执行外部函数变量');
                temp();
            });
        </script>
    

    结果:
    先把外部函数赋值给变量
    外部函数执行了
    再执行外部函数变量
    内部函数执行了

    以上情况,我们可以看到,内部函数不仅可以赋值给全局变量,还可以赋值给局部变量。

    就像面向对象的方法会用到类的字段,内部函数也会用到变量,接下来体验变量的作用域。

    变量的作用域

    内部函数变量

     <script type="text/javascript">
     
            function outerFunction() {
                function innerFunction() {
                    var temp = 0;
                    temp++;
                    console.log('内部函数的变量temp的值为:' + temp);
                }
                return innerFunction;
            }
     
            $(function() {
                var out1 = outerFunction();
                out1();
                out1();
     
                var out2 = outerFunction();
                out2();
                out2();
            });
        </script>
    

    结果:
    内部函数的变量temp的值为:1
    内部函数的变量temp的值为:1
    内部函数的变量temp的值为:1
    内部函数的变量temp的值为:1

    从中我们可以看出内部函数变量temp的生命周期:
    →当第一次执行内部函数,JavaScript运行时创建temp变量
    →当第二次执行内部函数,JavaScript垃圾回收机制把先前的temp回收,并释放与该temp对应的内存,再创建一个新的内部函数变量temp

    .....
    所以,每次调用内部函数,内部函数的变量是全新的。也就是说,内部函数的变量与内部函数同生共灭。
    □ 全局变量

     <script type="text/javascript">
            //全局变量
            var temp = 0;
            function outerFunction() {
                function innerFunction() {
                    temp++;
                    console.log('全局变量temp的值为:' + temp);
                }
                return innerFunction;
            }
     
            $(function() {
                var out1 = outerFunction();
                out1();
                out1();
     
                var out2 = outerFunction();
                out2();
                out2();
            });
        </script>
    
    

    结果:
    全局变量temp的值为:1
    全局变量temp的值为:2
    全局变量temp的值为:3
    全局变量temp的值为:4

    可见,全局变量供外部函数和内部函数共享。

    □ 外部函数变量

    <script type="text/javascript">
            function outerFunction() {
                var temp = 0;
                function innerFunction() {
                    temp++;
                    console.log('外部函数变量temp的值为:' + temp);
                }
                return innerFunction;
            }
     
            $(function() {
                var out1 = outerFunction();
                out1();
                out1();
     
                var out2 = outerFunction();
                out2();
                out2();
            });
        </script>
    

    结果:
    外部函数变量temp的值为:1
    外部函数变量temp的值为:2
    外部函数变量temp的值为:1
    外部函数变量temp的值为:2

    可见,外部函数的变量与外部函数同生共灭。

    以上情况,更接近与"闭包"的原型。有如下几个要素:
    1、外部函数
    2、外部函数变量
    3、内部函数

    当我们在外部函数之外调用内部函数的时候,这时的内部函数就叫做"闭包",可以理解为面向对象的实例方法。"闭包"与外部函数变量的"外部环境"是外部函数,他俩与外部函数同生共灭。

    一个外部函数中可以有多个内部函数,当调用"闭包"的时候,多个"闭包"共享外部函数变量:

      <script type="text/javascript">
            function outerFunction() {
                var temp = 0;
                function innerFunction1() {
                    temp++;
                    console.log('经innerFunction1,外部函数变量temp的值为:' + temp);
                }
                
                function innerFunction2() {
                    temp += 2;
                    console.log('经innerFunction2,外部函数变量temp的值为:' + temp);
                }
     
                return {'fn1' : innerFunction1, 'fn2' : innerFunction2};
            }
     
            $(function() {
                var out1 = outerFunction();
                out1.fn1();
                out1.fn2();
                out1.fn1();
     
                var out2 = outerFunction();
                out2.fn1();
                out2.fn2();
                out2.fn1();
            });
        </script>
    

    结果:
    经innerFunction1,外部函数变量temp的值为:1
    经innerFunction2,外部函数变量temp的值为:3
    经innerFunction1,外部函数变量temp的值为:4
    经innerFunction1,外部函数变量temp的值为:1
    经innerFunction2,外部函数变量temp的值为:3
    经innerFunction1,外部函数变量temp的值为:4

    jQuery中的"闭包"

    "闭包"在jQuery中最常见的应用是,当Document加载完毕再执行jQuery部分:

    <script type="text/javascript">
            $(document).ready(function() {
                var temp = 0;
     
                function innerFunction() {
                    temp++;
                    console.log(temp);
                }
     
                innerFunction();
                innerFunction();
            });
        </script>
    
    

    结果:
    1
    2

    可见,$(document).ready()的参数就是一个匿名外部函数,匿名函数内的函数是内部函数。

    把元素的事件也可看作是内部函数:

    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title></title>
        <script src="../Scripts/jquery-2.1.1.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function() {
                var counter = 0;
     
                $('#btn1').click(function(event) {
                    counter++;
                    console.log(counter);
                });
     
                $('#btn2').click(function(event) {
                    counter--;
                    console.log(counter);
                });
            });
        </script>
    </head>
    <body>
        <input id="btn1" type="button" value="递增"/>
        <input id="btn2" type="button" value="递减"/>
    </body>
    

    可见,2个input元素的click事件看作是内部函数,共享同一个外部函数变量counter。

    在循环体遍历中,把每次遍历的元素事件也可看作是内部函数:

    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title></title>
        <script src="../Scripts/jquery-2.1.1.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function() {
                for (var i = 0; i < 3; i++) {
                    $('<div>Print ' + i + '</div>').click(function() {
                        console.log(i);
                    }).insertBefore('#results');
                }
            });
        </script>
    </head>
    <body>
        <div id="results"></div>
    </body>
    

    页面呈现的结果如预期:
    Print 0
    Print 1
    Print 2

    可当点击每个div的时候,原本以为控制器台应该显示:0, 1, 2,但实际显示的始终是3,为什么?
    --i看作是匿名外部函数的"自由变量",当页面加载完毕后,i就变成了3。div的每次点击看作是内部函数的闭环,而所有的闭环都共享了值为3的这个变量。

    我们可以使用jQuery的each()方法来解决以上问题,遍历一个数组,每一次遍历元素值都不同:

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title></title>
        <script src="../Scripts/jquery-2.1.1.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function() {
                $.each([0, 1, 2], function(index, value) {
                    $('<div>Print ' + value + '</div>').click(function() {
                        console.log(value);
                    }).insertBefore('#results');
                });
            });
        </script>
    </head>
    <body>
        <div id="results"></div>
    </body>
    
    内存泄漏

    内存泄漏可以狭隘地理解为:应用程序中变量对应一块内存,由于某些原因(比如代码上的漏洞),始终没有对变量及时实施手动或自动垃圾回收,内存没有得到及时释放,造成内存泄漏。

    在JavaScript中,如果对象间的关系比较简单:

    1

    以上,A有一个属性指向B,B有一个属性指向C,当A被回收的时候,没有依赖B的对象,B随之被自动回收,C也被最终回收。

    当对象间的关系比较复杂,比如存在循环引用的时候,如下:

    2

    以上,A有一个属性指向B, B有一个属性指向C,而C又有一个属性指向B,B和C之间存在循环引用。当A被回收之后,B和C是无法自动被回收的,在JavaScript中应该手动处理回收。

    JavaScript闭包有可能会造成内存泄漏:

    →内部函数闭包引发的内存泄漏

       $(function() {
                var outerVal = {};
     
                function innerFunction() {
                    console.log(outerVal);
                }
     
                outerVal.fn = innerFunction;
                return innerFunction;
            });
    

    以上,outVal是在内存中的一个对象,而内部函数innerFunction同样是内存中的一个对象。对象outVal的属性fn指向内部函数,而内部函数通过console.log(outerVal)引用outVal对象,这样outVal和内部函数存在循环引用的情况,如果不手动处理,就会发生"内存泄漏"。

    如果,我们在内部函数中不显式引用outerVal对象变量,会造成"内存泄漏"吗?

    $(function() {
                var outerVal = {};
     
                function innerFunction() {
                    console.log('hello');
                }
     
                outerVal.fn = innerFunction;
                return innerFunction;
            });
    

    答案是:会的。因为,当内部函数被引用、调用的时候,即使内部函数没有显式引用外部函数的变量,也会隐式引用外部函数变量。

    →元素事件引发的内存泄漏

    在IE中,如下写法会造成内存泄漏:

    $(document).ready(function(){
        var button = document.getElementById("btn");
        button.onclick = function(){
            console.log('hello');
            return false;
        }
    });
    

    而如下JavaScript写法不会造成内存泄漏:

    function hello(){
        console.log('hello');
        return false;
    }
     
    $(document).ready(function(){
        var button = docuemtn.getElementById('btn');
        button.onclick = hello;
    });
    

    而在jQuery中,类似的写法就不用担心内存泄漏了,因为jQuery为我们做了自动处理来规避内存泄漏。

    $(document).ready(function(){
        var $button = $('#btn');
        $button.click(function(event){
            event.preventDefault();
            console.log('hello');
        });
    });
    
    总结

    与"闭包"相关的包括:变量的作用域、javascript垃圾回收、内存泄漏,需在实践多体会。

  • 相关阅读:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    类似-Xms、-Xmn这些参数的含义:
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    Java 虚拟机是如何判定两个 Java 类是相同的?
    互联网支付系统整体架构详解
    DTO
  • 原文地址:https://www.cnblogs.com/newcapecjmc/p/11840492.html
Copyright © 2011-2022 走看看