9.2Module(模块)模式
通常能够帮助我们清晰地分离和组织项目中的代码单元
js中实现模块的方法
1》对象字面量表示法
2》Module模式
3》AMD模式
4》CommonJS模块
5》ECMAScript Harmony模块
Module模式某种程度上是基于对象的字面量
9.2.1对象字面量
在对象字面量表示法中,一个对象被描述为一组包含在大括号{}中、以逗号分隔的name/value对。对象内的名称可以是字符串或标识符,后面跟着一个冒号。对象中最后的一个name/value对的后面不用加逗号,如果加逗号将会导致出错。
Var myObjectLiteral={
variableKey:variableValue;
functionKey:function(){
//
}
};
对象字面量不需要使用new运算符进行实例化,但不能用在一个语句的开头,因为开始的可能被解读为一个块的开始。在对象的外部,新成员可以使用如下赋值语句添加在字面量上,如:
myModule.property="some Value";
使用对象字面量有助于封装和组织代码,Module模式仍然使用对象字面量,但只是作为一个作用域函数的返回值。
var myModule={ myProperty:"somevalue", //对象字面量可以包含属性和方法 //例如,可以声明模块的配置对象 myConfig:{ useCaching:true, language:"en" }, //基本方法 myMethod:function(){ console.log("myMethod"); }, //根据当前配置输出信息 myMethod2:function(){ console.log("caching is:" + (this.myConfig.useCaching) ? "enabled":"disabled"); }, //重写当前配置 myMethod3:function(newConfig){ if(typeof newConfig==="object"){ this.myConfig=newConfig; console.log(this.myConfig.language); } } }; myModule.myMethod(); myModule.myMethod2();//打印出来的是enabled,没有加上前面的字符串 myModule.myMethod3({ language:"fr", useCaching:false });
9.2.2Module(模块)模式
Module模式最初被定义为一种在传统软件工程中为类提供私有和公有封装的方法。
在js中,Module模式用于进一步模拟类的概念,通过这种方式,能够使一个单独的对象用于公有/私有方法和变量,从而屏蔽来自全局作用域的特殊部分。产生的结果是:函数名与在页面上其他脚本定义的函数冲突的可能性降低。
模块模式的模板
var myNamespace=(function(){ //私有计数器变量 var myPrivateVar=0; //记录所有参数的私有函数 var myPrivateMethod=function(foo){ console.log(foo); } return{ //公有变量 myPublicVar:"foo", //调用私有变量和方法的公有函数 myPublicFunction:function(bar){ //增加私有计数器值 myPrivateVar++; //传入bar调用私有方法 myPrivateMethod(bar); } }; })();
9.2.2.1 私有
Module模式使用闭包封装“私有”状态和组织。它提供了一种包装混合公有/私有方法和变量的方式,防止其泄露至全局作用域,并与别的开发人员的接口发生冲突。通过该模式,只需返回一个公有API,而其他的一切则都维持在私有闭包里 。
这为我们提供了一个屏蔽处理底层时间逻辑的整洁解决方案,同时只暴露一个接口供应用程序的其他部分使用。该模式除了返回一个对象而并不是函数之外,非常类似于一个立即调用的函数表达式。
应该指出的是,在js中没有正真意义上的“私有”,因为js没有访问修饰符,因此我们使用函数作用域来模拟这个概念。在Module模式内:闭包声明的变量和方法只在该模式内部可用。但在返回对象上定义的变量和方法,则对外部使用者都是可用的。
var basketModule=(function(){ //私有 var basket=[]; function doSomethingPrivate(){ console.log("private"); } function doSomethingElsePrivate(){ // } //返回一个暴露出的公有对象 return{ //添加item到购物车 addItem:function(values){ basket.push(values); }, //获取购物车里的item数 getItemCount:function(){ return basket.length; }, //私有函数的公有形式别名, // doSomething:doSomethingPrivate自动调用doSomethingPrivate函数 doSomething:doSomethingPrivate, //获取购物车里所有item的价格总值 getTotal:function(){ var itemCount=this.getItemCount(),total=0; while(itemCount--){ total+=basket[itemCount].price; } return total; } }; })(); //basketModule返回了一个拥有公用API的对象 basketModule.addItem({ item:"bread", price: 0.5 }); basketModule.addItem({ item:"butter", price:0.3 }); console.log(basketModule.getItemCount()); console.log(basketModule.getTotal()); //会打印一个private和一个undefined,原因不明 console.log(basketModule.doSomething()); console.log(basketModule.basket);
basket模块的优点:
1》只有模块自身才能享有拥有私有函数的自由,因为它只会暴露我们输出的API。
2》鉴于函数往往已声明并命名,在试图找到哪些函数抛出异常时,这将使得在调试器中显示调用堆栈变得更容易。(没感觉)
3》根据环境,还可以让我们返回不同 的函数
9.2.2.3 示例
/** * counter的存在被局限在模块的闭包内,唯一能访问其作用域的代码是return中的2个函数 */ var testModule=(function(){ var counter=0; return{ incrementCounter:function(){ return ++counter; }, resetCounter:function(){ console.log("counter value prior to reset "+counter); counter=0; } }; })(); //增加计数器 testModule.incrementCounter(); //检查并重置计数器 testModule.resetCounter();
//引入混入 var myModule=(function(jQ,_){ function privateMethod1(){ jQ(".container").html("test"); } return{ publicMethod:function(){ privateMethod1(); } }; //引入JQuery和Underscore })(jQuery,_);