Javascript『模块化』(一)- 基础实现

1,175 阅读4分钟

Javascript的问题和模块化简介

JS是一个强大灵活的语言,但也是一种蛋疼的语言,最关键的是web开发方面你没得选,再多的无奈和不爽,也得学下去。

今天来讲JS的模块化,模块化的概念多得不得了,而且是学习高级js编程的前置条件,初学者很容易卡在这里,网上系统讲这个问题的文章我搜了一下确实不多。

模块化:

别的严肃一点的语言,比如c++,java,python什么的,甚至PHP,都有class类的概念,类可以把代码的作用域很自然的分开来,是面向对象编程的基础。javascript 没有的class,所谓模块化,也就是最小化的实现这个class的功能;

模块化以后的好处

其实也就是问『类』的好处都有啥:

    • 独立模块好维护
    • 有自己的命名空间,不污染不冲突
    • 模块可以复用

4种基础模块化的实现方法

要记住,js中唯一可以用来创建作用域的办法就是利用function;这里介绍4种最常见,最基础的模块化实现方法:

基础方法(一) : 匿名闭包函数

(function () {
               var first_name = 'Tom';
               var last_name = ' Cruise';
               function fullname(){
                    return first_name + last_name;
               }
                console.log(fullname());
                }());
            //Tom Cruise

有很多初学者会问,为什么function(){}() 最外面还要包个(),变成(function(){}())呢?还有就是function(){}后面为啥还跟一个括号呢?
因为加了最外面的括号,里面的东西就叫做表达式,是可以被直接执行的。
而不加最外面的括号的话,function(){}()会被理解成为一种声明,而直接声明匿名函数是会报错的。
function(){}后面还跟一个括号,是为了让function(){}里的内容立即执行;

好,回到正题,我们这样写有啥意义呢?

你把一段代码放到闭包里写,就不会污染全局的变量,里面的变量就只能自己用,外面读不到闭包内的变量,但是闭包里面可以读到全局的变量。

var global = "Woody";
        (function () {
           var first_name = 'Tom';
           var last_name = ' Cruise';
           function fullname(){
                return first_name + last_name;
           }
            console.log(fullname());
            console.log(global);
            }());
        console.log(first_name);
        //Tom Cruise
        //Woody
        //Uncaught ReferenceError: first_name is not defined

观察外面变量和里面变量的关系,这其实就是模块化了。

基础方法(二) : 全局引入

这种方法与第一种方法基本无差异,只不过闭包内读取全局变量是通过方法参数传进来的:

var global = {name:"Woody"};
        (function (global) {
           var first_name = 'Tom';
           var last_name = 'Cruise';
           function fullname(){
                return first_name + last_name;
           }
            console.log(fullname());
            console.log(global.name);
            }(global));

你看,基本上一模一样,只不过传参的这种模式,习惯上一般只引入一个全局变量(这个变量往往是一个对象),这样代码可读性大大提高,人家知道哦就一个变量是外面来的,其他的都是内部定义的。使用这种方法的典型例子就是Jquery。

基础方法(三) :对象接口

就是把这个需要模块化的部分搞成一个对象,使用的时候用对象化的方式调用,我们来看看怎么搞:

var myName = (function () {
           var first_name = 'Tom';
           var last_name = ' Cruise';
           return {
               'fullname': function(){
                return first_name + last_name;
               }   
           }
           }());
        myName.fullname() //Tom Cruise

关键就是在闭包里面,return一下,并且return出来的东西是一个对象,然后就可以在全局使用这个对象了。

基础方法(四) : 模块暴露

类似的,这个方法和方法三基本一模一样:

var myName = (function () {
           var first_name = 'Tom';
           var last_name = ' Cruise';
           var fullname = function(){
                return first_name + last_name;
               };
           return {
               'fullname': fullname
           }
           }());
        myName.fullname() //Tom Cruise

可以说是基本一样的,但是这样写,你可以把所有属性和方法保持私有,到return的时候,可以自由选择那些需要保留,哪些需要暴露出去。

问题来了

上述4种方法有一个共同点:用一个全局变量通过闭包的方式把一段代码全部包起来,因此创造了一个私有命名空间。这样写很常用,是很好的办法。但是它们有缺点:

  1. 不能管理依赖
    前面我们只说到一个闭包模块的情况,我们还没考虑到多个模块的情况。
    假设模块A依赖于模块X,在实际代码中,如果你把模块X放在了模块A的后面,就会报错。
    自动管理依赖,是所有编程语言需要解决的一个问题。

  2. 仍然会有命名空间的冲突闭包模块它们虽然作用域和全局分离开了,但是命名空间上来看,仍然处于根命名空间。比如我做了一个叫做myName的模块,然后引入了另一个人做的也叫myName的模块,那么就会起冲突。

为了解决这些问题,JSer们发明了各种眼花缭乱的模块化标准,欲知详情,请听下回分解。