javascript模块化编程(一)

版权声明:此文章转载自极客头条

如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com

随着嵌入网页的javascript代码越来越复杂,网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等......开发者不得不使用软件工程的方法,管理网页的业务逻辑。

Javascript模块化编程,已经成为一个迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。但是,Javascript不是一种模块化编程语言,它不支持" 类 "(class),更遑论"模块"(module)了。(正在制定中的 ECMAScript标准 第六版,将正式支持"类"和"模块",但还需要很长时间才能投入实用。)

Javascript社区做了很多努力,在现有的运行环境中,实现"模块"的效果。

以下是模块化编程的演变路径

一、原始写法

最简单的写法就是把不同的函数放在一起当成一个模块

function m1(){
    //do sthing
}
function m2(){
    //do sthing
}

上面的m1,m2组成一个模块。使用的时候直接进行调用。

缺点很明显:“污染”全局变量,无法保证不与其它模块的变量名之间发生冲突。而且无法看出模块成员之间的关系。

二、对象写法

为了解决上述问题,可以把模块成员都放到一个对象里面,一个模块就是一个对象

var module1 = {
    _count : 0,
    m1 : function (){
        //do sthing
    },
    m2 : function (){
        //do sthing
    }
};

上面的函数m1,m2和变量_count都封装在module1对象中。使用的时候就是调用这个对象的属性

module1.m1();

但这种方法的一个问题就是将对象中所有的模块成员都暴露了出来,内部状态可以被外部状态改写。比如:

module._count=5;

三、立即执行函数的写法

为了解决上述问题,我们采用立即执行函数的写法,能够达到不暴露模块成员的目的。

var module1 = (function(){
    var _count = 0;
    var m1 = function(){
        //...
    };
    var m2 = function(){
        //...
    };
    return {
        m1 : m1,
        m2 : m2
    };
})();

使用上面的写法,外部代码无法读取到内部的_count变量

console.log(module1._count);   //undefined

原因:这是因为立即执行函数的返回值是 m1:m1,m2:m2 ,并没有将 _count return出来。那么实际上module1是一个对象,对象中有两个方法,如图:

49.png

所以外部也是能够读取到m2的,如图:

50.png

module1就是Javascript模块的基本写法

module1就是Javascript模块的基本写法

module1就是Javascript模块的基本写法

重要的事情说三遍

下面,我们对这种写法进一步的加工

四、放大模式

如果一个模块很大,需要分成几个部分。或者一个模块需要继承另一个模块,这时就有必要采用“放大模式”

var module1 = (function (mod){
    mod.m3 = function () {
        //...
    };
    return mod;
})(module1);

上面代码为module1模块添加了一个新的方法 m3 ,然后返回一个新的module1模块

五、宽放大模式

在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用"宽放大模式"。

var module1 = ( function (mod){
    //...
    return mod;
})(window.module1 || {});

与"放大模式"相比,"宽放大模式"就是"立即执行函数"的参数可以是空对象。

作用:进行保底。不多传一个空对象的话,那么module1的值有可能出现 undefined ,调用 module1.XXX 时会报错。进行保底之后module1的值最不济只是得到一个空对象{},在调用 module1.XXX 时候不会报错。

六、输入全局变量

独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。

因此为了在模块内部调用全局变量,我们必须显式的把其它变量传入模块。

var module1 = (function ($, YAHOO) {
    //...
})(jQuery, YAHOO);

上面的module1模块需要使用jQuery库和YUI库,就把这两个库(其实是两个模块)当作参数输入module1。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。

想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。

关于作者

郝淼emily

重新开始,从心开始

我要评论

评论请先登录,或注册