这里记录模块,对模块的了解与认识
模块
我们可以使用函数和闭包来构造函数,模块是一个提供接口和隐藏实现的函数或对象,通过使用函数产生模块,我们几乎可以完全摒弃全局变量的使用,从而缓解这个JavaScript的最为糟糕的特性之一所带来的影响
举例来说,假定我们想要给String增加一个deentityify方法。它的任务是寻找字符串中的HTML字符实体并把它们替换为对应的字符。这就需要在一个对象中保存字符实体的名字和它们对应的字符。但我们该在哪里保存这个对象呢?我们可以把它放到一个全局变量中,但全局变量是魔鬼。我们可以把它定义在该函数内部,但是那会带来运行时的消耗,因为每次执行该函数的时候该字面量都会被求值一次。理想的方式是把它放入一个闭包,而且也许还能提供一个增加更多字符实体的扩展方法:
Function.prototype.method = function(name,func){
this.prototype[name] = func;
return this;
}
//给String增加一个deentityify方法,它的任务是寻找字符串中的HTML字符实体并把他们替换为对应的字符
String.method('deentityify',function(){
//字符实体表,它映射字符实体的名字到对应的字符
var entity = {
quot : '"',
lt : '<',
gt : '>'
};
//返回deentityify方法
return function(){
//这才是deentityify方法,它调用字符串的replace方法
//查找‘&’开头和‘;’结束的子字符串,如果这些字符串可以在字符实体表中找到
//那么就将该字符实体替换为映射表中的值,它用到了一个正则表达式
return this.replace(/&([^&;]+);/g,
function(a,b){
var r = entity[b];
return typeof r === 'string' ? r : a;
}
)
}
}())
请注意最后一行。我们用()云算法立刻调用我们刚刚构造出来的函数。这个调用所创建并返回的函数才是deentityify方法
document.writenln('<$quot;>'.deentityify()); //<">
模块模式利用了函数作用域和闭包来创建被绑定对象与私有成员的关联,在这个例子中,只有deentityify方法有权访问字符实体表这个数据对象
模块模式的一般形式是:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可访问到的地方。
使用模块模式就可以摒弃全局变量的使用。它促进了信息隐藏和其他优秀设计实践。对于应用程序封装,或者构造其他单例对象,模块模式非常 有效
模块模式也可以用来产生安全的对象。假定我们想要构造一个用来产生序列号的对象
var serial_marker = function(){
//返回一个用来产生唯一字符串的对象
//唯一字符串由两部分组成:前缀 + 序列号
//该对象包含一个设置前缀的方法,一个设置序列号的方法
//和一个产生唯一字符串的gensym方法
var prefix = '';
var seq = 0;
return {
set_prefix: function(p){
prefix = String(p);
},
set_seq: function(s){
seq = s;
},
gensym: function(){
var result = prefix + seq;
seq += 1;
return result;
}
}
}
var seqer = serial_marker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym(); // unique是"Q1000"
seqer包含的方法都没有用到this或that,因此没有办法损害seqer。除非调用对应的方法,否则没办法改变prefix或seq的值。seqer对象是可变的,所以它的方法可能会被替换掉,但替换后的方法依然不能访问私有成员。seqer就是一组函数的集合,而且那些函数授予特权,拥有使用或修改私有状态的能力
如果我们把seqer.gensym作为值传递给第三方函数,那个函数能用它产生唯一字符串,但却不能通过它来改变prefix或seq的值