浅谈javascript的模块化

在多次面试中被问了关于前端模块化的问题,一直没有答得完整,一方面是自己紧张,但更多的其实是自己对模块化没有足够的了解。借此机会,结合之前看过的文章重新梳理一下关于模块化的知识。

一,对模块化的需求

在javascript的初期,js文件加载都是使用<script>标签直接引入,简单直接,当时的js内容不多,功能简单,所以并无不妥。

到后来,随着网页开发技术的发展,对js的要求变得越来越高,文件数量越来越多,内容越来越复杂,文件之间开始产生依赖,单纯的直接引入已经不能处理。因此,javascript的模块化应运而生。

二,什么是模块化

软件工程中,在处理大型功能或者一系列的小功能的时候,会按照一定的规则把整个问题分成多个小部分来处理,一个部分处理一个特定的问题,这些小部分就叫模块。模块中对其他模块的依赖通过一定的规则引入,相互之间没有直接的影响。javascript在刚设计的时候并没有考虑到模块化的问题,在前人的不断的尝试下,出现了各种各样的模块化方式。

三,模块化方式的发展

一开始还没有CommonJS,AMD,CMD等规范的时候,前端们会使用原生的javascript来实现模块化:

如函数式:

function foo(){
    //some code
}
function bar(){
    //some code
}
缺点是函数在同一个文件内声明,会污染全局变量

用对象包装:

var foo={
    num:123,
    f1:function(){
        //some code
    },
    f2:function(){
        //some code
    }
}
用对象包装起来作为模块的话,只要保证每一个模块的名字不一样就能避免污染全局变量,缺点是在模块外部可以改变内部的内容,会造成安全上的问题:
foo.num=456;
使用立即执行函数(IIFE):

var foo=(function(){
    var num=123;
    function m1(){
        //some code
    };
    function m2(){
        //some code
    }
    return {fun1:m1,fun2:m2}
})();
这样返回一个对象就能保护在模块内定义的变量,只暴露出可供调用的函数。

四,CommonJS,AMD与CMD

随着前人不断的尝试,更科学的规范出现了:由nodejs定义的CommonJS,由requirejs定义的AMD和由seajs定义的CMD(这些规范都是相对应的js库流行起来之后官方总结出来的规范,并不是先出现规范再出现规范的实现)。CommonJS处理的是服务端的模块化问题,所以在这里不多做介绍。

因为之前用过requirejs,所以会主要讲AMD模式。
AMD模式,全称是Asynchronous Module Definition,中文译作异步模块定义。异步是关键词,require.js是AMD模式的实现,它解决了模块化问题中的两大问题:1.大量的依赖限制了相关程序的编写;2.传统文件加载方式造成的页面渲染阻塞。require会先把模块需要的模块加载,把相应处理的代码放在回调函数中,因此不会造成依赖模块未加载引发报错,然后,所有模块通过异步模式加载,不会对页面渲染造成影响。

看一个require的例子:

//模块定义name.js
define(['jquery'], function(){
    var name = 'wimenlo';
    function setName(){
        $("#name").html(name);
    }

    return {
        setName: setName
    };
});
//模块加载main.js
require(['name'], function (name){
  name.setName();
});

例子中:define(id?,dependencies?,factory)函数定义了一个模块,函数接受3个参数:

id(可选),表示该模板名称,若不设置,则默认为该模块文件的文件名;

dependencies(可选),表示该模块的依赖,数组;

factory,表示该模块的主题内容;

如例子,该例子定义了一个依赖jquery的模块,模块内有一个name变量,还返回了一个setName函数;

当我们需要加载使用该模块的时候,要使用require(dep,callback)函数,该函数接受两个参数:

dep,是所加载的模块的数组;

callback,当dep的模块加载完毕之后执行的处理函数;

如例子,调用了刚才定义的name模块,把模块以变量的形式传入处理函数中,然后调用其中的setName函数。

以上就是require的简单应用。

AMD风格的模块可以简单转化为CommonJS风格的模块,当然这是后话。

后面有时间会更详细地介绍require的实际使用。


CMD模式,是Sea.js所遵循的模块化规范,全称Common Module Definition(通用模块定义规范)。

其最主要的内容是全局函数define,在CMD规范中,我们一般这样定义一个模块:

define(function(require,exports,module){
    //some code
});
define函数其实跟AMD的define函数类似,可以接受三个参数,不过只有上面这种写法才能称作遵循CMD规范。

传入的函数称为模块的构造方法(factory),该方法默认传入三个参数,下面稍微介绍一下:

require:接受模块标识,用来获取其他模块的接口,也就是本模块的依赖。

var a=require(“./a”);
a.foo();

模块标识默认为该模块文件的路径。

exports:该参数代表本构造方法对外提供的模块接口。

exports.foo=function(){
    //some code
}
这样,其他模块使用require的时候就能加载到这个foo函数(也可以使用return来返回接口)。

注意,对exports进行重新定义是没用的:

exports={
    bar:1,
    baz:2
}
exports只是module.exports的一个引用,并不能重新赋值,下面会介绍module的用法。

module是一个对象,储存了当前模块相关的一些属性和方法,CMD方法不在define中定义模块标识,但可以在factory中调用module.id来定义模块标识。

module.exports是模块对外提供的接口,exports是它的引用,不能通过改变exports参数来改变提供的接口,但可以用module.exports来该变。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值