zoukankan      html  css  js  c++  java
  • Js----闭包

    1、闭包的概念:(我找了很多,看大家的理解)

      A:闭包是指可以包含自由(未绑定到特定对象)变量的代码块;

               这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。

      B:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

      C:能够读取其他函数内部变量的函数。

          或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。

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

      E:闭包就是能够读取其他函数内部变量的函数。闭包可以简单理解成“定义在一个函数内部的函数“。

       在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

      F:如果一个函数访问了它的外部变量,那么它就是一个闭包。

         注意,外部函数不是必需的。通过访问外部变量,一个闭包可以维持(keep alive)这些变量。在内部函数和外部函数的例子中,

         外部函数可以创建局部变量,并且最终退出;但是,如果任何一个或多个内部函数在它退出后却没有退出,

         那么内部函数就维持了外部函数的局部数据。

      G:当内部函数 在定义它的作用域 的外部 被引用时,就创建了该内部函数的闭包 ,如果内部函数引用了位于外部函数的变量,

          当外部函数调用完毕后,这些变量在内存不会被 释放,因为闭包需要它们。

      H:一个持有外部环境变量的函数就是闭包

    2、闭包的特性:

      封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口;

      持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在。

    3、闭包的用途:

    function f1(){
            var n = 123;
            function f2(){    //f2是一个闭包
                alert(n)
            }    
            return f2;
        }

      A、读取函数内部的变量
      B、让这些变量的值始终保持在内存中。不会再f1调用后被自动清除。
      C、方便调用上下文的局部变量。利于代码封装。
      原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。

    4、闭包的理解:

    
    function init() {
        var name = "Chrome";    //创建局部变量name和局部函数alertName
    
        function alertName() {  //alertName()是函数内部方法,是一个闭包
            alert(name);        //使用了外部函数声明的变量,内部函数可以访问外部函数的变量
        }
        alertName();
    }
    init();
                                //一个变量在源码中声明的位置作为它的作用域,同时嵌套的函数可以访问到其外层作用域中声明的变量
    
    
    function outFun(){
        var name = "Chrome";
        function alertName(){
            alert(name);
        }
        return alertName;   //alertName被外部函数作为返回值返回了,返回的是一个闭包
    }
    
    var myFun = outFun();
    myFun();
    /*
    闭包有函数+它的词法环境;词法环境指函数创建时可访问的所有变量。
    myFun引用了一个闭包,闭包由alertName()和闭包创建时存在的“Chrome”字符串组成。
    alertName()持有了name的引用,
    myFunc持有了alertName()的的访问,
    因此myFunc调用时,name还是处于可以访问的状态。
     */
    
    function add(x){
        return function(y){
            return x + y;
        };
    }
    
    var addFun1 = add(4);
    var addFun2 = add(9);
    
    console.log(addFun1(2)); //6
    console.log(addFun2(2));  //11
    //add接受一个参数x,返回一个函数,它的参数是y,返回x+y
    //add是一个函数工厂,传入一个参数,就可以创建一个参数和其他参数求值的函数。
    //addFun1和addFun2都是闭包。他们使用相同的函数定义,但词法环境不同,addFun1中x是4,后者是5

    5、闭包的应用场景:

      A:闭包应用场景之setTimeout

    //原生的setTimeout传递的第一个函数不能带参数
        setTimeout(function(param){
            alert(param)
        },1000)
    
    
        //通过闭包可以实现传参效果
        function func(param){
            return function(){
                alert(param)
            }
        }
        var f1 = func(1);
        setTimeout(f1,1000);

      B:闭包应用场景之回调

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title></title>
        <link rel="stylesheet" href="">
    </head>
    <style>
        body{
            font-size: 12px;
        }
        h1{
            font-size: 1.5rem;
        }
        h2{
            font-size: 1.2rem;
        }
    </style>
    <body>
    
        <p>哈哈哈哈哈哈</p>
        <h1>hhhhhhhhh</h1>
        <h2>qqqqqqqqq</h2>
    
        <a href="#" id="size-12">12</a>
        <a href="#" id="size-14">14</a>
        <a href="#" id="size-16">16</a>
    
    <script>
        function changeSize(size){
            return function(){
                document.body.style.fontSize = size + 'px';
            };
        }
    
        var size12 = changeSize(12);
        var size14 = changeSize(14);
        var size16 = changeSize(16);
    
        document.getElementById('size-12').onclick = size12;
        document.getElementById('size-14').onclick = size14;
        document.getElementById('size-16').onclick = size16;
        //我们定义行为,然后把它关联到某个用户事件上(点击或者按键)。我们的代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上
    </script>
    </body>
    </html>

      C:闭包应用场景之封装变量

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>闭包模拟私有方法</title>
        <link rel="stylesheet" href="">
    </head>
    <body>
    <script>
        //用闭包定义能访问私有函数和私有变量的公有函数。
        var counter = (function(){
            var privateCounter = 0; //私有变量
            function change(val){
                privateCounter += val;
            }
            return {
                increment:function(){   //三个闭包共享一个词法环境
                    change(1);
                },
                decrement:function(){
                    change(-1);
                },
                value:function(){
                    return privateCounter;
                }
            };
        })();
    
        console.log(counter.value());//0
        counter.increment();
        counter.increment();//2
        //共享的环境创建在一个匿名函数体内,立即执行。
        //环境中有一个局部变量一个局部函数,通过匿名函数返回的对象的三个公共函数访问。
    
    </script>
    </body>
    </html>

      D:闭包应用场景之为节点循环绑定click事件

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title></title>
        <link rel="stylesheet" href="">
    </head>
    <body>
    
        <p id="info">123</p>
        <p>E-mail: <input type="text" id="email" name="email"></p>
        <p>Name: <input type="text" id="name" name="name"></p>
        <p>Age: <input type="text" id="age" name="age"></p>
    
    <script>
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                var item = infoArr[i];
                document.getElementById(item.id).onfocus = function(){
                    showContent(item.content)
                }
            }
        }
        setContent()
        //循环中创建了三个闭包,他们使用了相同的词法环境item,item.content是变化的变量
        //当onfocus执行时,item.content才确定,此时循环已经结束,三个闭包共享的item已经指向数组最后一项。
    
    
    
        /**
         * 解决方法1     通过函数工厂,则函数为每一个回调都创建一个新的词法环境
         */
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function callBack(content){
            return function(){
                showContent(content)
            }
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                var item = infoArr[i];
                document.getElementById(item.id).onfocus = callBack(item.content)
            }
        }
        setContent()
    
        /**
         * 解决方法2        绑定事件放在立即执行函数中
         */
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                (function(){
                    var item = infoArr[i];
                    document.getElementById(item.id).onfocus = function(){
                        showContent(item.content)
                    }
                })()//放立即执行函数,立即绑定,用每次的值绑定到事件上,而不是循环结束的值
            }
        }
        setContent()
    
        /**
         * 解决方案3        用ES6声明,避免声明提前,作用域只在当前块内
         */
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                let item = infoArr[i];      //限制作用域只在当前块内
                document.getElementById(item.id).onfocus = function(){
                    showContent(item.content)
                }
            }
        }
        setContent()
    </script>
    </body>
    </html>

    6、闭包的优缺点:

      缺点:

          A:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,

           在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

          B:闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,

          把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,

                       不要随便改变父函数内部变量的值。

      优点:

          A:减少创建全局变量 减少传递给函数的参数量

          B:避免全局变量的污染

             C:私有成员的存在

          D:希望一个变量长期驻扎在内存当中

    7、闭包对页面的影响:

      通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优雅、更简洁的表达出代码;

            在某些方面提升代码的执行效率。

     

  • 相关阅读:
    【转载】CentOS 6.3(x86_64)下安装Oracle 10g R2 天高地厚
    Oracle查看表空间和删除表空间 天高地厚
    获取android手机的定位信息(转)
    android里pull解析xml文件
    google map 开发去掉图片阴影
    ubuntu12.04配置android开发环境遇到的问题
    google code中下载你的项目源码
    android无法自动生成R文件
    android中ocr解决方案(tesseract)
    自定义Android标题栏TitleBar布局(转)
  • 原文地址:https://www.cnblogs.com/liaohongwei/p/10560309.html
Copyright © 2011-2022 走看看