外观模式(Facade)为子系统中的一组接口提供了一个一致的界面,此模块定义了一个高层接口,这个接口值得这一子系统更加容易使用。
外观模式不仅简化类中的接口,而且对接口与调用者也进行了解耦。外观模式经常被认为开发者必备,它可以将一些复杂操作封装起来,并创建一个简单的接口用于调用。在JavaScript中,也经常使用对底层结构兼容性做统一的封装,来简化用户的使用。
二、外观模式的引入
需求:为页面上一个元素添加一个click事件,实现隐藏功能。
上代码
<div id="panel" style="height: 100px; background: red;"></div>
<script type="text/javascript">
//1、绑定一个点击事件,控制panel的隐藏。
document.onclick = function(e){
e.preventDefault();
if (e.target == document.getElementById("panel")) {
hidePanel();
}
}
function hidePanel(){
alert("隐藏了panel");//没有具体实现。
};
</script>
你觉得上述代码有问题吗?
1、首先,onclick事件是DOM0事件,相当于给document绑定了一个事件方法,假如团队中有人也给时间绑定一个方法,那么,你的事件就被覆盖了。
document.onclick = function(e){
//开发人员重新给document绑定click事件,前面的事件将会被覆盖
}
所以应该用DOM2级事件处理机制程序addEventListener().
2、低版本(IE8以下)IE浏览器处理事件不是 addEventListener(),而是attachEvent().而且事件处理名字也不一样,只能用onclick方法,那么如何处理呢?
三、外观模式
我们有什么好方法吗?答案是显而易见的,可以用外观模式来封装他们。
举个例子:饭堂的炒菜师傅不会因为你预定了一份烧鸭和一份白菜就把这两样菜炒在一个锅里。他更愿意给你提供一个烧鸭饭套餐。同样在程序设计中,我们需要保证函数或者对象尽可能的处在一个合理粒度,毕竟不是每个人喜欢吃烧鸭的同时又刚好喜欢吃白菜。
外观模式还有一个好处是可以对用户隐藏真正的实现细节,用户只关心最高层的接口。比如在烧鸭饭套餐的故事中,你并不关心师傅是先做烧鸭还是先炒白菜,你也不关心那只鸭子是在哪里成长的。
外观模式的实现:
function addEvent(dom, type, fn){
if (dom.addEventListener) {
dom.addEventListener(type,fn, false);//W3C浏览器
}else if (dom.attachEvent) {
dom.attachEvent("on"+type, fn);//低版本IE
}else{
dom["on"+type] = fn;//两种方法都不支持的处理。
}
}
这样就可以放心的使用了。
var panel = document.getElementById("panel");
addEvent(panel,"click",function(){
alert("绑定第一个事件");
});
addEvent(panel,"click",function(){
alert("绑定第二个事件");
});
addEvent(panel,"click",function(){
alert("绑定第三个事件");
});
四、进一步探讨
回想之前的代码,我还会发现一些兼容性问题,比如e.target
与e.preventDefault()
,下面我们继续完善代码
//获取事件对象
var getEvent = function(event){
return event || window.event;//标准浏览器返回event,低版本IE返回window.event;
}
//获取元素
var getTarget = function(event){
var event = getEvent(event);
return event.target || event.srcElement;//标准浏览器返回event.target,低版本IE返回event.srcElement;
}
//阻止事件默认行为
var preventDefault = function(event){
var event = getEvent(event);
if (event.preventDefault) {
event.preventDefault();//标准浏览器
}else{
event.returnValue = false;//IE浏览器
}
}
有了上述方法,我们就可以兼容的方式来完善刚开始的代码
document.onclick = function(e){
preventDefault(e);
if (getTarget(e) == document.getElementById("panel")) {
hidePanel();
}
}
function hidePanel(){
alert("隐藏了panel");//没有具体实现。
};
五、封装自己小型代码库
再来一个简单的例子,说白了就是用一个接口封装其它的接口,很多代码库就是通过外观模式来封装多个功能,简化底层代码的操作,比如下方:
var EventUtil = {
getEvent: function(event){ //获取事件对象
return event || window.event; //return event?event:window.event;
},
getTarget = function(event){//获取目标元素
var event = getEvent(event);
return event.target || event.srcElement;
},
preventDefault = function(event){//阻止事件默认行为
var event = getEvent(event);
if (event.preventDefault) {
event.preventDefault();//标准浏览器
}else{