功能:可以用来划分命名空间。单体可以用作命名空间把自己的代码组织在一个全局变量名下,这样可以减少网页中全局变量的数量(在网页中使用全局变量有风险);可以在多人开发时避免代码的冲突(使用合理的命名空间);可以用来把相关代码组织在一起以便日后好维护等等。
基本结构:
var Singleton= {
name:'Darren',
method1:function(){
//code
},
init:function(){
//code
}
}
上述使用单体的方法就是用一个命名空间包含自己的所有代码的全局对象。但是上例的方式,对象Singleton的所有成员是公开的,外部对Singleton的所有成员都能访问以及修改。在执行到变量Singleton时,会加载(实例化)自身,即非惰性加载。
使用闭包
为了私有化Singleton的成员,我们可以使用闭包,只公开需要公开的成员:
var Singleton = (function(){
var attr = 1,
fn = function(){};
return { //return的都是公有成员,其他私有
method : function(){ fn(); },
getAttr : function(){ return attr; }
};
})();
惰性单体
前面提到的单体模式的实现都是在脚本加载时即创建的。当一个单体对象需要加载大量数据时,在需要时再创建单体对象会更好。例如我们在弹窗或者一些对话框弹出时需要给网页增加一层遮罩:
var createMask = function(){
var mask;
return function(){
return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
}
}()
上述代码在创建mask之前判断一下是否存在mask,如果不存在再创建一个mask。
分支技术
将浏览器之间的差异封装到动态方法,适用于解决浏览器之间的差异:
var Singleton = (function(){
var objectA = {
methodA1 : function(){...},
methodA2 : function(){...},
};
var objectB = {
methocB1 : function(){...},
methodB2 : function(){...}
};
return (someCondition) ? objectA : objectBb;
})();
功能:一是简化类的接口;二是消除类与使用它的客户代码之间的耦合。门面模式是几乎所有JavaScript库的核心原则。通过创建一些便利的方法让复杂系统变得更加简单易用。使用门面模式,我们可以将一些繁琐的底层方法或系统放在门面模式中,提供一个更为简单的接口方法,这样用户只需使用这些简单的接口方法去创建自己的应用。
var addEvent =function(el,type,fn){
if(window.addEventListener){
el.addEventListener(type,fn);
}elseif(window.attachEvent){
el.attachEvent('on'+type,fn);
}else{
el['on'+type] = fn;
}
}
这个就是一个JavaScript中常见的事件监听器函数,这个函数就是一个基本的门面。它将不同浏览器对事件监听支持的函数不同,抽取出一个简单的接口,这样我们需要使用事件监听时,只需要调用简单的接口,而不需要再去考虑不同浏览器的差异。
适用场景:
应对JavaScript内置函数在不同浏览器中的不同表现。可以把这些差异抽取到门面方法,这样对外可以提供一个更一致的接口。上述的addEvent就是一个简单的门面,相同的,我们也可以把removeEvent、getEvent、getTarget等方法写成门面,构建自己的代码库。如下,我们队常用的事件模型进行了封装:
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
getEvent: function(event){
return event ? event : window.event;
},
getTarget: function(event){
return event.target || event.srcElement;
},
preventDefault: function(event){
if (event.preventDefault){
event.preventDefault();
} else {
event.returnValue = false;
}
},
removeHandler: function(element, type, handler){
if (element.removeEventListener){
element.removeEventListener(type, handler, false);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
功能:可以用来在现有接口和不兼容的类之间进行适配,适配器可以被添加到现有代码中以协调两个不同的接口。在设计类的时候,往往会遇到有些接口不能与现有的API一同使用的情况。借助于适配器,可以不用直接修改这些类也能使用它们。使用这种模式的对象又叫包装器,因为他们是在用一个新的接口包装另一个对象。
//假如有一个3个字符串参数的函数,但是现在拥有的却是一个包含三个字符串元素的对象,那么就可以用一个配置器来衔接二者
var clientObject = {
str1:'bat',
str2:'foo',
str3:'baz'
}
function interfaceMethod(str1,str2,str3){
alert(str1)
}
//配置器函数
function adapterMethod(o){
interfaceMethod(o.str1, o.str2, o.str3);
}
adapterMethod(clientObject)
//adapterMethod函数的作为就在于对interfaceMethod函数进行包装,并把传递给它的参数转换为后者需要的形式。
适配器模式的工作机制是:用一个新的接口对现有类的接口进行包装。