Module

关于模块化:

  • 可重用
  • 封装了变量和function,和全局的namaspace不接触,松耦合
  • 只暴露可用public的方法,其它私有方法全部隐藏
  1. 基本的模块化

对象写法

1
2
3
4
var Module = {
m1: function(){},
m2: function(){}
}

减少 Global 上的变量数目,起到了一点点的命名空间作用
同样缺点明显: 会暴露所有模块成员,内部状态可以被外部改写

匿名闭包 :IIFE 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
var Module = (function(){
var _private = "safe";
var m1 = function(){
/* code */
}

return {
m1: m1
}
})()

Module.m1();
Module._private; // undefined

可以很好的保护私有变量,通过return来设置公开的方法
动态添加方法的时候比较麻烦,且无法修改内部私有变量.

引用全局变量

1
2
3
4
5
6
7
8
9
10
11
12
var Module = (function($){
var _$body = $("body"); // can use jQuery
var m1 = function(){
/* code */
}

return {
m1: m1
}
})(jQuery)

Module.m1();

全局变量当成一个参数传入到匿名函数然后使用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Module = (function () {
var m = {},
_private = "";

function privateM1(data) {
/* code */
}

m.attr = _private;
m.method = function (data) {
privateM1(data);
};

return m;
} ());

全局变量Module带有2个可访问的属性Module.method和Module.,除此之外,其它代码都在匿名函数的闭包里保持着私有状态。

module 扩展

将一个功能分离成多个文件

1
2
3
4
5
6
var Module = (function (m) {
m.method = function () {
/* code */
};
return m;
} (Module));

松耦合扩展

解决顺序问题

1
2
3
4
5
6
var Module = (function (m) {
m.method = function () {
/* code */
};
return m;
} (Module || {}));

首个被引入的文件会创建Module命名空间(就是个JavaScript对象),而且所有使用这种结构的后续文件都只是重用此现有实例。

紧耦合扩展

紧耦合扩展限制了加载顺序,但是提供了我们重载的机会

1
2
3
4
5
6
7
8
9
10
11
var Module = (function (m) {
var oldMethod = m.AddMethod;

m.AddMethod = function () {
/* code
可通过oldMethod调用旧的方法
*/

};

return m;
} (Module));

缺点:

  • 全局作用域下容易造成变量冲突
  • 文件只能按照 script 的书写顺序进行加载
  • 开发人员必须主观解决模块和代码库的依赖关系
  • 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

模块规范

  • COMMONJS
    CommonJS中,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的,除非定义为global对象的属性。
    通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或 module.exports 来导出需要暴露的接口
    CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。
    more
  • AMD
    AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块。RequireJS遵守AMD规范
    模块通过 define 函数定义在闭包中,格式如下
    define(id?, dependencies?, factory);
    id 是模块的名字,它是可选的参数。
    dependencies 指定了所要依赖的模块列表,它是一个数组,也是可选的参数,每个依赖的模块的输出将作为参数一次传入 factory 中。如果没有指定 dependencies,那么它的默认值是 [“require”, “exports”, “module”]
    factory 是最后一个参数,它包裹了模块的具体实现,它是一个函数或者对象。如果是函数,那么它的返回值就是模块的输出接口或值。
    more

  • CMD
    AMD 很相似,尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。

    • 依赖就近,延迟执行
    • 可以很容易在 Node.js 中运行
  • AMD VS CMD
    • CMD推崇依赖就近,可以把依赖写进你的代码中的任意一行,代码在运行时,首先是不知道依赖的,需要遍历所有的require关键字,找出后面的依赖。具体做法是将function toString后,用正则匹配出require关键字后面的依赖。显然,这是一种牺牲性能来换取更多开发便利的方法。
    • 而AMD是依赖前置的,换句话说,在解析和执行当前模块之前,模块作者必须指明当前模块所依赖的模块,无需遍历整个函数体找到它的依赖,因此性能有所提升,缺点就是开发者必须显式得指明依赖——这会使得开发工作量变大
      大势所趋:
  • Browserify - CommonJS In Browser
    Browserify是一个node.js模块,主要用于改写现有的CommonJS模块,使得浏览器端也可以使用这些模块
  • Webpack - Module Bundler
    Webpack 是一个模块打包器。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。

requirejs是一种在线”编译” 模块的方案,相当于在页面上加载一个AMD 解释器,以便于览器能够识别 define、exports、module,而这些东西就是用于模块化的关键。
而browserify / webpack,则是一个预编译模块的方案。它是预编译的,不需要在浏览器中加载解释器。

  • ES6 Module
    ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西

    模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
    more