随着BS架构的发展,网站逐渐变成了互联网应用程序,嵌入网络的JavaScript代码越来越庞大,越来越复杂(业务逻辑处理或用户交互很多写在前端)。网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等。。开发者不得不使用软件工程的方法,管理网页的业务逻辑。因此JavaScript模块化编程已经成了一个迫切的需求,理想的情况下是开发者只需要实现核心的业务逻辑,其他业务处理都可以加载别人已经写好的模块,做到明确分工而不会相互影响。

但是,JavaScript却不是一种模块化编程语言,它不支持类(class),更别说模块(module)了。虽然ECMAScipt正在谋划支持和推广类和模块的概念,但要实际投入生产还是遥遥无期,只能自己另外想办法。为此JavaSript社区做了很多努力,努力在现有的运行环境中,利用现有的资源,实现模块化的效果。

模块化的原始写法

模块的定义就是实现特定功能的一组方法,只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块了。

function f1() {
// doSomething
} function f2() {
// doSomething
}

上面的函数f1()和f2()共同组成了一个模块,使用的时候直接通过函数名调用就行了。这种做法的缺点很明显,既污染了全局变量(f1和f2处在全局的上下文栈中,属于window的属性),也无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接的关系。

模块化的对象写法

为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。

var module1 = {
status: 233, f1: function() {
// doSomething
}, f2: function() {
// doSomething
}
}

上面的函数f1()和函数f2()都封装在了module1对象里,使用的时候就是通过访问module1对象的属性。

module1.f1();

但是,这样的写法会暴露所有的模块成员,且内部的状态可以被外部改写。

module1.status = 666;

模块化的立即执行函数写法

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

var module1 = (function() {
var status = 233; var f1 = function() {
// doSomething
}; var f2 = function() {
// doSomething
};
})();

使用这样的写法,外部的代码就无法读取到内部的变量。

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

这种写法,就是JavaScript模块化的基本写法,后面的实现基本上都是依照这个思路。

模块化的放大模式

如果一个模块很大,就会要拆分成几个小的模块,或者一个模块需要继承另一个模块,这个时候就要采用放大模式(Augmentation)。

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

这里为module1模块添加了一个新函数f3(),然后返回新的module1模块。也就是把旧的对象传进来,给这个对象添加属性,然后返回添加了属性后的对象,相当于扩展。

模块化的宽放大模式

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

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

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

模块化的全局变量输入

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

为了在模块内部调用全局变量,必须显式地将其他变量输入模块。

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

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

模块化的几种规范

模块化的前提是要遵循同一套规范,否则模块之间的调用会十分困难。

JavaScript官方没有模块化的规范,目前通用的民间规范主要有CommonJS(服务端js模块化的规范,NodeJS是这种规范的实现)、AMD(Asynchronous Module Definition异步模块定义,RequireJS遵循此规范)和CMD(Common Module Definition,通用模块定义,SeaJS遵循此规范)。

模块化在服务端的规范:CommonJS

2009年,美国程序员Ryan Dahl创造了node.js项目,将JavaScipt语言用于服务器端编程(后端)。这标志着JavaScipt模块化编程正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。

node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性的方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载并调用模块中提供的方法:

var math = require('math');
math.add(2, 3); //

更多的用法这里就不说了,只需要知道CommonJS是使用require()函数加载模块就行了。

模块化规范从服务端到客户端的发展

有了服务端的模块化之后,大家就想要客户端的模块化了。而且最好两者能兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是由于一个重大局限,使得CommonJS规范不适用于浏览器环境。这是因为,在上面的代码中,调用math的方法必须要在math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

这对于服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是对于浏览器来说,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,可能会导致浏览器处于假死状态。

因此浏览器端的模块化不能使用同步加载(Synchronous),只能使用异步加载(Asynchronous)。这就是AMD规范诞生的背景。

模块化在客户端的规范:AMD

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

AMD也采用require()语句加载模块,不同于CommonJS的是,它要求两个参数:

require([module], callback);

第一个参数[moudle],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是这样:

require(['math'], function(math) {
math.add(2, 3);
});

这样,math.add()与math模块的加载就不是同步的,浏览器也不会发生假死的状况。所以很显然地是AMD比较适合浏览器环境。应用AMD规范的主要有require.js。

模块化在客户端的规范:CMD

CMD是SeaJS在推广过程中对模块定义的规范化产出。

AMD和CMD的区别:

1.对于依赖的模块,AMD是提前执行,CMD是延迟执行。CMD推崇的是as lazy as possible,即尽可能得懒加载(延迟加载),即在需要得时候才加载。

2.CMD推崇依赖就近,AMD推崇依赖前置。

// CMD
define(function(require, exports, module) {
var a = require('./a');
a.doSomething();
var b = require('./b'); // 依赖可以就近书写
b.doSomething();
}) // AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething();
b.doSomething();
})

3.AMD的API默认是一个当多个用,CMD的API则是严格区分,推崇职责单一。比如在AMD里,require分全局和局部,而在CMD里则没有全局require,而是根据模块系统的完备性,提供seajs.use来实现模块系统的加载启动。CMD里,每个API都简单存粹。

模块化的优点总结

1.解决了命名的冲突问题。多人开发的场景下,容易出现命名冲突,模块化通过内部封装与外部隔离能有效防止命名冲突的问题。

2.解决了文件的依赖问题,使文件易于管理。如果有很多js文件相互依赖,依赖关系和加载顺序都是让人头冷的问题。使用模块化就可以很好地实现依赖管理(使用依赖都要提前声明)。

3.提高代码的可读性。各个模块各自完成自己的功能,专司其职,除了问题也会便于维护。

4.提高代码的复用性。可以抽提特定的通用功能作为一个通用的模块。

"可是怎么办,想起你的时候,心还是会疼。"

javascript模块化编程思想、实现与规范的更多相关文章

  1. Javascript模块化编程(二):AMD规范

    Javascript模块化编程(二):AMD规范   作者: 阮一峰 原文地址:http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_d ...

  2. Javascript模块化编程(二):AMD规范(转)

    这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可以更方便地使用别人的代码,想要 ...

  3. Javascript模块化编程(二):AMD规范 作者: 阮一峰

    声明:转载自阮一峰的网络日志 这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可 ...

  4. Javascript模块化编程(二):AMD规范【转】

    作者: 阮一峰 日期: 2012年10月30日 这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为 ...

  5. javascript模块化编程(AMD规范的加载器)

    关于AMD规范可以参考阮一峰的这篇文章Javascript模块化编程(二):AMD规范 简单来说,AMD规范就是异步方式加载模块的一种方式,避免因为模块加载过慢而导致浏览器“假死”. 先贴一个学习地址 ...

  6. (转)Javascript模块化编程(二):AMD规范

    转自 ruanyifeng 系列目录: Javascript模块化编程(一):模块的写法 Javascript模块化编程(二):AMD规范 Javascript模块化编程(三):Require.js的 ...

  7. Javascript模块化编程(二)AMD规范(规范使用模块)

    这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块,先想一想,为什么模块很重要?接下来为您详细介绍,感兴趣的朋友可以了解下啊.今天介绍如何规范地使用模块. 七.模块 ...

  8. Javascript模块化编程(二):AMD规范(转)

    这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可以更方便地使用别人的代码,想要 ...

  9. (转)Javascript模块化编程(二):AMD规范

    这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可以更方便地使用别人的代码,想要 ...

随机推荐

  1. 使用Vuejs 开发chrome 插件的注意事项

    chrome 插件的开发其实并不难,web开发者可以使用 html, css, javascript 轻松的开发实用的 chrome 插件. 一个好的 chrome 插件可以提高我们的开发效率,甚至方 ...

  2. HTTP 状态码及含义

    来自 Koa.js 官方文档中关于设置请求响应的部分 response.status=,列出了从 1xx ~ 5xx HTTP 状态码及含义,现摘录如下: 100 "continue&quo ...

  3. Protobuffer学习文档

    官方EN:https://developers.google.com/protocol-buffers/docs/pythontutorial 中文:https://cloud.tencent.com ...

  4. Python 使用 PyMysql、DBUtils 创建连接池,提升性能

    转自:https://blog.csdn.net/weixin_41287692/article/details/83413775 Python 编程中可以使用 PyMysql 进行数据库的连接及诸如 ...

  5. python:王思聪究竟上了多少次热搜?

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 朱小五 凹凸玩数据 PS:如有需要Python学习资料的小伙伴可以加 ...

  6. MySQL的5大引擎及优劣之分

    MySQL提供了多个不同的存储引擎,其中5大引擎有:innodb,myisam,memory,merge,csv. 其中优点有:灾难恢复性好.支持事务.使用行级锁.支持外键关联.支持热备份 缺点如下: ...

  7. Go文件内容追加问题

    今天在用文件追加时,遇到点小问题,一直没法追加,起初是这样的 fp,err1 = os.OpenFile(filename,os.O_APPEND,0666) 上面这个死活加不了,后面改成 fp,er ...

  8. 解决sublime快捷键回车换行问题

    鼠标右键sublime 以管理员身份运行 打开首选项里面的按键绑定用户 将下面的代码粘贴复制 { "keys": ["enter"], "comman ...

  9. Xcode修改工程文件名字

    http://stackoverflow.com/questions/8262613/renaming-xcode-4-project-and-the-actual-folder

  10. 导入部署 hand

    差价导入部署步骤如下: 执行视图,包. 定义消息,验证的时候使用(XXC1003DFM_BI_001 到 XXC1003DFM_BI_007,含中英文). 通用导入设置: 电子表元数据管理,含导入正确 ...