• Javascript 笔记与总结(1-5)闭包


    【例1】

    <script>
    function t1(){
        var age = 20;
        function t2(){
            alert(age);
        }
        return t2;
    }
    var tmp = t1();
    var age = 99;
    tmp();
    </script>

    弹出 20

    【分析】

    function t1(){
        var age = 20;
        function t2(){
            alert(age);
        }
        return t2;
    }

    在大部分的语言中,t1 被调用执行,则申请内存,并把其局部变量 push 入栈,t1 函数执行完毕,内部的局部变量,随着函数的退出而销毁,导致 age = 20 的局部变量消失。

    因此,t1(); 执行完毕之后,按 C 语言的理解,t1 内的局部变量都释放了。

    function t1(){
    
        var age = 20;
        function t2(){
            alert(age);
        }
    return t2;
    }

    在 js 中,t1 执行过程中,又生成了 t2,

    而从作用域上来说,t2 能访问到 age = 20,

    于是 “ age = 20 ” 没有消失,而是被 t2 捕捉到了,

    同时 “ age = 20 ” 与返回的 t1 函数形打了个包返回来了,成了一个 “ 环境包 ”,

    这个包属于 t2,所以叫 “ 闭包 ”。

    t2 把周围的环境打了包,也就是说 t2 是由自己的生态环境的,

    即 即使t1 执行完毕,通过 t2 ,依然能访问该变量:这种情况,函数并非孤立的函数,甚至把其周围的变量环境,形成了一个封闭的环境包,共同返回

    一句话概括:函数的作用域取决于声明时,而不取决于调用时

    【例2】

     1 function prev(){
     2     var leg = "Alexis";
     3     var arsenal_leg = function(){
     4         return leg;
     5     } 
     6     return arsenal_leg; //前半赛季阿森纳的大腿是桑切斯
     7 }
     8 
     9 function after(){    //来到了后半赛季
    10     var leg = "Giroud";
    11     var arsenal_leg = prev();    //调用prev(),arsenal_leg 函数来到了后半赛季
    12     alert(arsenal_leg());
    13 }
    14 after();    //谁是大腿
    15 
    16 </script>

    弹出:Alexis

    【分析】:

    line:14 执行 after();

    line:12 alert arsenal_leg();

    line:11 aesenal_leg() = prev();

    line:6  return  arsenal_leg();

    line:3  函数声明 return leg;【真正执行的函数】

    line:2  var leg = "Alexis";【作用域要从 函数声明之时寻找】

     

    例3闭包计数器,多人开发 js 程序,需要一个全局的计数器

    解决方案① 设立一个全局变量

    window.cnt = 0;

    调用 ++window.cnt

    这个方案可行但是,污染了全局变量;其次比人的程序中,也可能会含有 window.cnt = ***; 则该计数器就会被损坏(所以要避免使用全局变量)

    方案② 使用闭包维护一个别人污染不到的变量做技术器

    <script>
    
    function counter(){
        var cnt = 0;
        var cnter = function (){
            return ++cnt;
        }
        return cnter;
    }
    
    var inc = counter();
    alert(inc());
    alert(inc());
    alert(inc());
    
    </script>

    弹出 3 次,分别是:1,2,3

    简化上面的程序:

    <script>
    
    var inc = (function counter(){
        var cnt = 0;
        return function (){
            return ++cnt;
        }
    })();
    
    alert(inc());
    alert(inc());
    alert(inc());
    
    </script>

    再改版上面的程序(inc 依然是全局变量。在工作中,一般避免全局污染或冲突):

    方案 ① 统一放在一个全局对象上,如 jQuery -> $

    <script>
    
    $ = {}; 
    //模拟jQuery
    $.cnt = (function(){
        var cnt = 0;
        return function cnter(){
            return ++cnt;
        }
    })();
    
    alert($.cnt());
    alert($.cnt());
    alert($.cnt());
    
    </script>

    方案 ② 每个人使用自己的命名空间(其实就是把自己的变量、函数都放在一个对象里)

    <script>
    
    var Dee = {}; //一般用姓名做命名空间
    Dee.cnt = (function(){
        var cnt = 0;
        return function cnter(){
            return ++cnt;
        }
    })();
    
    alert(Dee.cnt());
    alert(Dee.cnt());
    alert(Dee.cnt());
    
    </script>

    【例4】要求:点击 4 个 li,分别弹出 0,1,2,3

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <ul>
            <li>阿森纳</li>
            <li>切尔西</li>
            <li>曼城</li>
            <li>利物浦</li>
        </ul>
    </body>
    <script>
    
    for(var i = 0, lis = document.getElementsByTagName("li"), len = lis.length; i < len; i++){
        lis[i].onclick = (function(i){
            return function(){
                alert(i);
            }
        })(i);
    }
    
    </script>
    </html>      

     【错误的写法】(会弹出 4,4,4,4):

    <script>
    
    for(var i = 0, lis = document.getElementsByTagName("li"), len = lis.length; i < len; i++){
        lis[i].onclick = function(){
            alert(i);
        }
    }
    
    </script>

    【错误的写法分析】:

    (回答者杨志):

    " 这个for循环会立即执行完毕,那么当onclick触发时,inner function查找变量 i 时,会在AO+scope中找,AO中没有,scope中的变量i已经成为 link.length. ",

    在运用了闭包之后,

    " 这时,如果inner function被触发,他会从自己的AO以及scope(outer function的AO 和 window scope)中找寻变量i. 可以看到outer function的AO中已经包

    含了i,而且对于这个for循环,会有对应有N个(function(){})() 被创建执行。所以每个inner function都有一个特定的包含了变量 i 的outer function。这样

    就可以顺利输出0,1,2,3 。

    结论: 我们可以看到,闭包其实就是因为Scope产生的,所以,广义上来讲,所有函数都是闭包。"

    " 所谓的异步函数,是包括ajax,事件触发,setTimeout , setInterval等回调的函数。

    当解析文档流时如果遇到js代码,会立即执行。如果这期间有异步函数被触发,会被存放在队列结构中。当之前的代码执行完毕后,浏览器查看此队列,如有待执行

    函数,则执行;没有则等待触发。

    所以,那个i 值会成为最大的那个。因为for循环在异步函数被执行前,已经跑完了。"

    参考:关于 Js 循环添加事件时闭包的影响有哪些解法?

  • 相关阅读:
    原码, 反码, 补码 详解
    位移运算符
    ASP.NET中httpmodules与httphandlers全解析
    MySQL count
    真正的能理解CSS中的line-height,height与line-height
    IfcEvent
    IfcWorkCalendarTypeEnum
    IfcSingleProjectInstance
    转换模型
    IfcTypeProduct
  • 原文地址:https://www.cnblogs.com/dee0912/p/4433714.html
Copyright © 2020-2023  润新知