JavaScript中的模块化
将代码组织到一个类中的重要原因是,让代码更加“模块化”,可以实现重用。但类不是唯一的方式。
模块化的目标是支持大规模的程序开发,处理分散源中代码的组装,并且能让代码正确运行。很多前端框架包括了模块系统,如DOJO,CommonJS,SeaJS,使用require函数。
1 类中的模块
1.1 用做命名空间的对象
在模块创建过程中避免污染全局变量的一种方法是使用一个对象作为命名空间。它将函数和值作为命名空间对象属性存储起来(可以通过全局变量引用),而不是定义全局函数和变量。如下例中的NotSet就有contains、toString、equals等方法。
// 可用做任何抽象方法
function abstractmethod() { throw new Error("abstract method"); }
function AbstractSet() {throw new Error("Can't instantiate abstract classes"); }
AbstractSet.prototype.contains = abstractmethod;
var NotSet = AbstractSet.extend(
function NotSet(set) {this.set = set;},
{
contains: function(x) {return !this.set.contains(x);},
toString: function(x) {return this.set.toString();},
equals: function(that) {
return that instanceof NotSet && this.set.equals(that.set);
}
}
)
基于“保持干净的全局命名空间”的观点,有一种更好的做法是将相似(功能相关)的类定义为一个单独的全局对象:
var sets = {};
sets.NotSet = sets.AbstractSet.extend(...);
// 只有一个全局sets,通过它来调用
var s = new sets.NotSet(set);
// 更深层的命名空间
var sets = com.mrli.collections.sets;
用做私有命名空间的函数
有些辅助函数或方法,不需要对外调用,是私有对象(java中的private),不希望在外部是可访问的。可以将模块定义在某个函数的内部来实现。
例子1
var Set = (function invocation() { // 注意invocation并没有全局创建对象,只是一个函数表达式,如果没有invocation它就是一个匿名函数
function Set() { // 这个构造函数是局部变量
this.values = {}
this.n = 0;
tjhis.add.apply(this, arguments);
}
Set.prototype.contains = function(value) {
return this.values.hasOwnProperty(v2s(value));
}
// 内部函数,不用定义成_v2s,或者加set前缀,对外部不可见
function v2s(val) {
return val;
}
// 将Set从命名空间中导出
return Set;
} ()); // 这里的"()" 即执行前面定义的invocation函数,定义函数后立即执行
例子2:bootstrap源码
bootstrap源码
( https://github.com/twbs/bootstrap/blob/v4-dev/dist/js/bootstrap.js ),有如下方式:
第18行,使用了定义并立即执行的函数
第19,21行等,使用了内部函数
第36,189,3650行等,使用了立即执行的函数,不同的是传入了jQuery
Bootstrap源码2.png
2 模块化框架
目前有很多模块化前端框架,如CommonJS、国产Sea.js等,它们更像是一种约定。尤其像angularjs1.x则是自带模块化功能,完全构建了自己的生态帝国。angularjs的出现,像是一个big
boom,可以说是震撼了前端圈。近年也涌现出了一大批优秀的前端,其中不乏国内的优秀框架,如React, vue, avalon等。
2.1 CommonJS(CMD)
2.2 Sea.js(AMD)
国产框架,创始人是淘宝前端玉伯
网友评论