分享

javascript 学习笔记之模块化编程

 昵称10504424 2013-10-31

题外:

  进行web开发3年多了,javascript(后称js)用的也比较多,但是大部分都局限于函数的层次,有些公共的js函数可重用性不好,造成了程序的大量冗余,可读性差(虽然一直保留着注释的习惯,但是最后发现注释远远不够),影响了页面的加载速度和性能。去年开始着手对既有前端脚本进行重构和优化,查阅了很多技术大牛分享的资料,也比较系统的阅读了一遍《javascript权威指南》,js模块化编程深深的吸引了我,它改变了我编写js脚本程序的方式,同时也让代码的可读性和可维护性进一步增强。

下边就根据自己学习和实践过程中对js模块化编程的理解,分享一下我的经历,希望对您有所帮助:

  大家都知道,js中的变量(variable)有其作用范围,比如:函数里用var定义的变量在函数外是看不到的,而定义在函数外面的变量(不能有没有var修饰)均是全局变量,在js程序的任何位置都可以访问。嗯,实际上我们在工作过程中,业务逻辑比较多,而一个业务逻辑包含多个函数,函数之间共享使用某个变量,这样问题就来了,如果另外一个业务逻辑不小心定义了或者修改了这个变量,就会造成这个全局变量被污染,前一个业务逻辑就会出现脏读,过程测试如下:

一个很长的页面脚本程序包含两个子业务处理过程1和2,业务处理程序1需要定义两个函数和一个变量,一个函数设置变量,一个函数读取输出变量,如下:

 其他业务处理程序脚本:

 业务处理程序2开始,逻辑处理也定义了两个函数和一个变量,一个函数设置变量,一个函数读取变量进行其他处理,不幸的是,这个全局变量采用了同业务逻辑1相同的名字:

 程序过程在进行逻辑2后再进行逻辑1,此时出现了意外:

 输出结果如下:

 

很明显,实际输出的结果并不是期望的结果,此外还有另外一种情况,如果某个js脚本程序被共享为一个共用的脚本块,在多个地方调用(引入)这个脚本块时,也会很容易出现这个问题。

而模块化编程(Module)的出现就解决了这个问题,除此之外模块化编程还有其他几个特点:

1. 维护一个干净前端脚本的变量环境,保护一定作用范围内定义的全局变量不被范围外程序的污染;

2. 前端脚本程序的可重用性大大提高,可读性和可维护性进一步增强;

3. 可以组合多个module脚本,抽象成一个公共的脚本库,提高代码的开发效率;

前面说过,函数内部定义的变量函数外看不到(即不可用),为了保护变量环境的作用域,这正是我们需要的结果,故把整个业务处理逻辑扔到一个函数里实现就可以实现一个模块的定义,改写上面逻辑1和逻辑2的代码如下:

 输出结果如下:

由上图可知,在模块化编程下,每个模块内部使用的共用变量都很好的被保护起来了,不在收到外面其他逻辑处理的干扰,但是上述过程需要我们定义两个函数模块,如果我们不想额外定义任何中间变量,我们可以采用匿名函数来重新实现上述过程,代码改写如下:

 上面的是用匿名函数实现的模块化封装,输出的结果同实体函数时一样,是不是比实体函数时更加简洁了?!

注:上述过程中我们在每个模块中返回了this对象,是因为我们需要在后续的逻辑中调用该模块中的函数,如果在实践过程中模块处理程序不需要被外部逻辑调用,而只是在模块内部输出结果即可的话,我们只需返回模块最终处理的结果值或者不需要返回语句,依据具体情况具体分析。

通过上述的例子我们可以总结出模块化的一般思路:

1. 把相关联的一系列函数及变量的定义放在一个函数(匿名函数也行)中即可形成一个模块,模块中的变量和函数的作用域仅限于模块内部,外部无法直接调用,模块可以返回既定逻辑的处理结果。

2. 如果需要在模块外部提供调用模块中函数或者变量的接口,则需要将模块中函数或变量的定义用this标记,然后在模块最后返回这个this对象(函数中的this对象指的是window对象)。

模块化的编程思路如下:

另:大部分web开发的后台语言都采用C#或者java,熟悉这两种语言的童鞋都知道,它们内部封装了很多函数库(包),C#中要用using引入,java中要用import引入,这些库或者包都是把一系列相关联的函数、变量、类等对象封装到一个命名空间中,方便后续调用的更加方便、清晰,javascript也可以实现这种命名空间式的封装,拿之前的web版扫雷小游戏为例,游戏中定义了四个类(即四个模块,模块整体作为一个对象,可根据需求扩展更多):PlayBombGame、BombObjectList、BombObject、BombImgObject,我们可以把这四个模块对象封装到一个叫games.BombGame的命名空间中,代码如下:

 调用游戏接口初始化时如下:

当然,子命名空间games.BombGame的初始化还有其他几种方法,代码如下:

 

模块化编程的举例(例1,文档元素指定点插入的通用方法):

复制代码
 1 var Insert = (function() {
 2     //判断insertAdjacentHTML的支持性,如果支持,直接返回对象。
 3     if (document.createElement("div").insertAdjacentHTML) {
 4         return {
 5             before: function(e, h) { e.insertAdjacentHTML("beforebegin", h); },
 6             after: function(e, h) { e.insertAdjacentHTML("afterend", h); },
 7             atStart: function(e, h) { e.insertAdjacentHTML("afterbegin", h); },
 8             atEnd: function(e, h) { e.insertAdjacentHTML("beforeend", h); }
 9         };
10     }
11     //根据要添加的类容创建文档碎片
12     function fragment(html) {
13 
14         var elt = document.createElement("div");
15         var flag = document.createDocumentFragment();
16         elt.innerHTML = html;
17 
18         while (elt.firstChild) {
19             flag.appendChild(elt.firstChild);
20         }
21         return flag;
22     }
23     
24     var Insert = {
25 
26         before: function(elt, html) { elt.parentNode.insertBefore(fragment(html), elt); },
27         after: function(elt, html) { elt.parentNode.insertBefore(fragment(html), elt); },
28         atstart: function(elt, html) { elt.insertBefore(fragment(html), elt.firstChild); },
29         atend: function(elt, html) { elt.appendChild(fragment(html)); }
30     };
31     //将新的方法绑定到元素的原型链中,以便元素对象可以直接调用插入方法
32     Element.prototype.insertAdjacentHTML = function(pos, html) {
33         switch (pos) {
34             case "beforebegin": return Insert.before(this, html);
35             case "afterend": return Insert.after(this, html);
36             case "afterbegin": return Insert.atstart(this, html);
37         }
38     };
39     //返回对象
40     return Insert;
41 } ());
复制代码

(例2,文档初始化事件的通用封装):

~~~以上是我对js模块化编程的理解,如有纰漏,还请各位技术大牛指出完善~~~

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多