JS模块化编程

本文探讨了在ES6之前,如何使用ES5进行模块化编程,包括模块化的目的、常见方法及其局限性,如命名冲突和动态加载问题。介绍了使用闭包实现的模块化方式,以及对异步模块定义(AMD)规范的介绍,强调AMD规范在解决复杂依赖关系和动态加载方面的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这部分是针对过去ES5的,ES6里面已经可以实现很好的模块化理念,但是考虑到目前仍有许多基于ES5的前端架构项目,还是有必要把这些写下来。想看干货的可以直接拉到最下

Javascript不是一种模块化编程语言,它不支持"类"(class),更遑论"模块"(module)。

模块化的目的:

更好的代码组织方式、动态加载、避免命名冲突、更好的依赖处理、解耦和复用。

如何模块化?

第一种,可能也是平时大家最常用的一种方式,缺点也显而易见。

//函数m1()和m2(),组成一个模块。
//污染全局变量

function m1(){
    //...
  }
function m2(){
    //...
  }

于是乎,为了不污染全局变量,有人做了如下工作:

//对象写法:暴露所有模块成员,没有动态加载

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

这种看着感觉不错,还能有属于自己的模块变量 _count,简直就像个温馨的小家!然而所有的模块成员对外部是可见的,并且无法支持动态加载,即无法按需处理依赖关系。那么接下来,熟悉闭包的同学可能会提出下面这种方式。

//使用"立即执行函数"(Immediately-Invoked Function Expression,IIFE),
//可以达到不暴露私有成员的目的。

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

上面的例子,从模块的角度已经非常实用了,唯一的缺陷可能就是无法实现动态加载依赖。想象一下,你刚学会了这种新的模块化JS写法,你理所当然的希望把你当前的前端项目用这种模块化方式管理起来,于是你把现有的系统功能封装成一个个模块,并把他们写成了单独的js文件,通过index.html统一引入。一切看上去非常的美好,但是还是遇到了问题!

//这段代码依次加载多个js文件。
//加载时停止网页渲染
//严格保证加载顺序
//代码维护困难。

    <script src="Moudle1.js"></script>
  <script src="Moudle2.js"></script>
  <script src="Moudle3.js"></script>
  <script src="Moudle4.js"></script>
  <script src="Moudle5.js"></script>
  <script src="Moudle6.js"></script>

你的index.html可能看上去是这样的,你的某个模块停止了运行,检查发现Moudle2中引用到了Moudle3的方法,但是此时Moudle3.js还未加载,系统提示未找到模块对象。

实际上当模块数量增多之后,随着复杂的依赖关系,顺序的调整变得逐渐不可控,维护的成本几何增加,是时候使用动态加载来解放我们的工作了!

什么是AMD?

 AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

// 定义:新模块的名字,依赖的模块,实际定义
define('moduleB',['moduleA'],callback);
		
//请求:
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    	// some code here
});

AMD是一种规范!AMD是一种规范!AMD是一种规范!

事实上,目前有许许多多的js框架都已经实现了AMD规范。

接下来,我们可以试着自己实现一个遵循规范的动态加载实例。

//一个实现动态加载模块化的例子
var global={};
global.defs = {};
//通过id,对相应模块进行实例化
var instantiate = function (id) {
    var actual = global.defs[id];
    var dependencies = actual.deps;
    var definition = actual.defn;
    var len = dependencies.length;
    var instances = new Array(len);
    for (var i = 0; i < len; ++i)
        instances[i] = dem(dependencies[i]);
    var defResult = definition.apply(null, instances);
    if (defResult === undefined)
        throw 'module [' + id + '] returned undefined';
    actual.instance = defResult;
};
//定义模块,将模块ID与它的依赖、定义绑定
var def = function (id, dependencies, definition) {
    if (typeof id !== 'string')
        throw 'module id must be a string';
    else if (dependencies === undefined)
        throw 'no dependencies for ' + id;
    else if (definition === undefined)
        throw 'no definition function for ' + id;
    global.defs[id] = {
        deps: dependencies,
        defn: definition,
        instance: undefined
    };
};
//通过一个id,获取这个id对应的模块的实例,如果该实例不存在,则对其实例化
var dem = function (id) {
    var actual = global.defs[id];
    if (actual === undefined)
        throw 'module [' + id + '] was undefined';
    else if (actual.instance === undefined)
        instantiate(id);
    return actual.instance;
};
//加载请求的模块,在加载完成后的环境中运行代码
var req = function (ids, callback) {
    var len = ids.length;
    var instances = new Array();
    for (var i = 0; i < len; ++i)
        instances.push(dem(ids[i]));
    callback.apply(null, instances);
};
var define = def;
var require = req;
var demand = dem;


//定义一个数学模块
define(
    'math',
    [],
    function () {
        var obj={};
        obj.run=function () {
            console.log("你获得了数学能力");
        }
        console.log("数学能力以加载");
        return obj;
    }
);

//定义一个物理模块
define(
    'physics',
    [],
    function () {
        var obj={};
        obj.run=function () {
            console.log("你获得了物理能力");
        }
        console.log("物理能力以加载");
        return obj;
    }
);
//定义一个研究模块
define(
    'research',
    ["math","physics"],
    function (math,physics) {
        var obj={};
        obj.run=function () {
            math.run();
            physics.run();
            console.log("你获得了研究能力");
        }
        console.log("研究能力以加载");
        return obj;		
    }
);

//调用研究模块,在调用的时候,会动态加载研究模块的依赖,即物理模块和数学模块。
 require(['research'],function (research) {
        research.run();
    });

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值