• 闭包用法:经典案例


    闭包用法:经典案例

    学习一样技能,最终是想把它投入运用。我们从JS函数的最基础用法,一直研究到作用域链、闭包,这个过程消耗了我们大量的心血,那么闭包到底能用在哪些场景里面呢?下面将使用逐个枚举的方式给出运用闭包的典型战例。

    请注意,以下的例子都是应用闭包的典型场景,当然如果你愿意,也可以把它叫做“代码模式”。深入理解,甚至记住这些场景,将会让你的闭包技法如有神助。

    获取Table中被点击的行

    4.53

    代码:

    <html>

             <title>Ext江湖</title>

             <meta http-equiv="Content-Type" content="text/html" ; charset="utf-8">

             <script type="text/javascript" src="closure_example.js"></script>

             <body onload="myEffect()">

                       <table id="mytab" border="1">

                                <tr>

                                         <td>1</td>

                                         <td>2</td>

                                         <td>3</td>

                                         <td>4</td>

                                         <td>5</td>

                                </tr>

                                <tr>

                                         <td>1</td>

                                         <td>2</td>

                                         <td>3</td>

                                         <td>4</td>

                                         <td>5</td>

                                </tr>

                                //这里重复5<tr>,结构和上面一样

                       </table>

                       <div id="console" style="background:#ffff00"></div>

             </body>

    </html>

    closure_example.js的代码如下:

    function myEffect(){

             var console=document.getElementById('console');

             var tab=document.getElementById('mytab');

             var trs=tab.getElementsByTagName('tr');

             for(var i=0;i<trs.length;i++){

                       var tr=trs[i];

                       tr.onmouseover=function(){

                                this.style.background="#ff0000";

                       }

                       tr.onmouseout=function(){

                                this.style.background="#ffffff";

                       }

                       tr.onclick=(function(){

                                var rowNum=i;

                                return function(){

                                         console.innerHTML="点击了第"+rowNum+"";

                                }

                       })();

             }

    }

    解析:

    因为有这一句<body onload="myEffect()">,所以在body加载完成之后,myEffect()这个函数就被执行。myEffect做的事情很简单,它给每一个tr标签都添加了3个事件监听函数:onmouseroveronmouseoutonclick。前两个函数非常简单,无须解释。亮点在于第3个函数:

     

    tr.onclick=(function(){

             var rowNum=i;

             return function(){

                       console.innerHTML="点击了第"+rowNum+"";

             }

    })();

    从整体看,这是一个“自执行”函数,最终被注册给onclick事件的是内部return的这个匿名函数。那么,为什么要这么做呢?比如,和上面两个函数一样,做成这样可不可以呢?

    tr.onclick= function(){

             console.innerHTML="点击了第"+i+"";

    };

    注意,i是外层那个for循环里面定义的循环变量。你可以自己测试这个代码,最终结果是:无论你点击哪一行,结果都是“点击了第6行”。那么为什么会出现这种结果呢?这是因为在for循环的过程中,i始终代表的是同一个变量。虽然你看似给每个tr都注册了一个onclick函数,但是里面那个i最终指向的是同一个东西,它是随着for循环变化的。所以,在for循环结束之后,i将会一直是6。那么如何在for循环结束之后,让i一直保留for循环中对应的次序呢?这就是上面那个return函数的作用了。里面用一个局部变量var rowNum=i;i的值“缓存”起来,然后即使外层的“自执行”函数退出,内部return出来的匿名函数仍然可以访问到对应顺序的值。

    有了本章第3节对函数“作用域链”的研究,rowNum这个外层函数的局部变量被缓存在哪里,这种技法为什么能起作用,就无须多言了吧?

    模拟多线程

    4.54

    HTML代码:

    <html>

             <title>模拟多线程</title>

             <meta http-equiv="Content-Type" content="text/html" ; charset="utf-8">

             <body>

                       <button name="添加线程" value="添加线程" onclick="addThread()">添加

                                线程</button>

             </body>

             <script type="text/javascript" src="sim_thread.js"></script>

    </html>

    脚本代码:

    //这里是一个简单的DIV工具,用来创建DIV

    function DivUtil(){}

             DivUtil.prototype.counter=0;

             DivUtil.prototype.creatDiv=function(){

                       var div=document.createElement('div');

                       div.style.background='#ffff00';

                       div.id=this.counter++;

                       document.body.appendChild(div);

                       return div;

             }

             var divUtil=new DivUtil();

     

             //这里是“线程”类

             Thread=function(){}

             Thread.prototype.start=function(){

                       var div=divUtil.creatDiv();

                       if(div.id>=10){

                                div.innerHTML="只允许起10个线程,看看你的CPU,撑到爆!";

                                return;

                       }

                       var num=div.id;

                       setInterval(function(){

                                div.innerHTML=""+div.id+"个线程运行中..."+(num++);

                       },50);

             }

     

             //工具函数,添加线程

             function addThread(){

                       var thread=new Thread();

                       thread.start();

             }

    运行效果如图4-91所示。

     

    4-91  模拟多线程

    解析:

    这是一个非常有趣的例子,看起来就像启动了多个“线程”,核心的代码是这段:

    var num=div.id;

    setInterval(function(){

             div.innerHTML=""+div.id+"个线程运行中..."+(num++);

    },50);

    因为闭包的缘故,在定时器setInterval所执行的函数中,可以一直访问外层函数中的局部变量num

     

    ——本段文字节选自《EXT江湖》

    图书详细信息:

    http://www.cnblogs.com/broadview/archive/2012/01/20/2327735.html

  • 相关阅读:
    ==和equals
    Fastjson toJSONString 碰到的一个问题
    Java之旅之第一个Java项目[Sping+ Spring MVC + MyBatis] 项目框架
    Java之旅之第一个Java项目[Sping+ Spring MVC + MyBatis] 项目配置
    Java之旅之第一个Java项目[Sping+ Spring MVC + MyBatis] org.springframework.web相关源码
    Java之旅之第一个Java项目[Sping+ Spring MVC + MyBatis] 项目背景
    centos6 yum源不可用问题解决
    ORACLE修改最大连接数
    Docker学习笔记(二):Docker镜像
    Mysql5.7慢日志时间与系统时间相差8小时问题的解决
  • 原文地址:https://www.cnblogs.com/broadview/p/2340282.html
Copyright © 2020-2023  润新知