面向对象的模块化编程
模块是一个独立的JS文件,模块文件可以包含一个类定义、一组相关的类、一个实用函数库、一些待执行的代码
模块化的目标:支持大规模的程序开发,处理分散源代码的组装,并能让代码正确执行;不同的模块必须避免修改全局上下文,模块应当尽可能少的定义全局标识;
在模块创建过程中避免污染全局变量的一种方法是使用一个对象作为命名空间('容器');
为了保持干净的全局命名空间,将'容器'类定义为一个单独的全局对象,如:var sets = {};
这个sets对象就是模块的命名空间,并且将每个其它类/变量/方法等都定义为这个对象的属性,可以将这些属性封装起来私有化,也可以通过对外开放接口来达到从外部访问该属性;
调用命名空间内的属性:
(一): var s1 = new sets.Set1(4,5); //直接使用来创建类Set2的实例
(二):var s2 = sets.Set2; //导出类Set1到s1 然后就可以创建实例了 var s3 = new s2(4,5);
多层嵌套的命名空间:
var collections;
if(!collections) collections = {};
collections.sets = {};
collections.sets.A_Class = function(){...}; //在collections.sets命名空间内定义类A_Class
最顶层的命名空间通常用来标识模块作者或组织,可通过网站域名来标识,避免命名冲突,如com.biadu.collections.sets;
可以将整个模块导入:var sets = com.biadu.collections.sets;也可以导入命名空间内单独的类;
按照约定,模块的文件名,目录结构应当和命名空间相匹配:
如: 使用命名空间com.biadu.collections.sets的模块应当在这个文件中:com/biadu/collections/sets.js;
一个模块往往会对外导出(开放)一些公共API,其他人通过这些API来使用这个模块,它包括函数类属性方法;但具体实现模块的一些额外的辅助函数和方法不需要对外开放,在模块外部不可见不可访问;
模块函数(私有命名空间):用一个模块函数包裹模块的所有属性函数方法,并立即执行模块函数;模块函数也可以有名字,但它是用函数表达式定义的,因此这个名字不是全局变量,用法:
( function namespace(){......}() );最外层括号表示是函数表达式,而非声明语句;
一旦将模块代码封装进该模块函数,就需要导出其公共API:
方法一: 将模块函数返回的构造函数赋值给一个全局变量,这样这个变量就可以调用该构造函数指代的类的属性了
方法二:通过return{...};来返回要对外开放的属性,返回给全局变量;
方法三:将模块函数当做构造函数,通过new来调用,然后将要对外开放的属性赋值到this对象中;
方法四:在模块函数里直接设置那个对象的属性;
//====第一种
var Set = (function namespace(){
function Set(){
this.n = 0;
this.add.apply(this,arguments);
}
Set.prototype.contains = function(values){
return v2s(values);
};
Set.prototype.add = function(x,y){return x+y};
function v2s(val){return val*2;}
return Set;
}());
var ss = new Set(1,2,3);
ss.n;
ss.contains(8);
ss.add(5,6);
//====第二种
var collections;
if(!collections) collections = {};
collections.sets = (function namespace1(){
//.........定义代码
var aPropoty=1, b=2;
var aFunction = function(){return aPropoty+b;};
return{
a1:aPropoty,
a2:b,
f:aFunction
};
}() );
collections.sets.f();
collections.sets.a1;
//====第三种
var collections1;
if(!collections1) collections1 = {};
collections1.sets = (new function namespace2(){
var aPropoty=1, b=2;
var aFunction = function(){return aPropoty+b;};
this.a1 = aPropoty;
this.a2 = b;
this.f = aFunction;
}() );
collections1.sets.f();
collections1.sets.a1;
collections1.sets.a2;
//====第四种
var collections2;
if(!collections2) collections2 = {};
collections2.sets = {};
( function namespace(){
var aPropoty=1, b=2;
var aFunction = function(){return aPropoty+b;};
collections2.sets.a1 = aPropoty;
collections2.sets.a2 = b;
collections2.sets.f = aFunction;
}() );
collections2.sets.a1;
collections2.sets.a2;
collections2.sets.f();