javaScript设计模式-Facade(外观)模式

Facade(外观)模式

Facade 模式为更大的代码体提供了一个方便的更高层次接口,能够隐蔽其底层的真实复杂性。可以把它想象成是简化 API 来展示给其他开发人员,通常都是可以提高可用性

Facade 是一种结构型模式,在 jQuery 等 JavaScript 库中进程可以看到,尽管一个实现可能支持具有广泛行为的方法,但却只有一个“外观”或这些方法的有限抽象能够提供给公总使用。

每当使用 jQuery 的$(el).css() 或 $(el).animate()方法时,实际上我们时在使用 Facade:一种更简单的公有接口,是我们不必手动在 jQuery 核心中调用更多内部很多方法以便实现某些行为。这也避免了手动与 DOM API 交互并维护状态变量的需要

jQuery 核心方法应该被认为时中间抽象。对于开发人员来说,更直接的事是 DOM API,外观可以使 jQuery 库很容易使用

这是一个未优化的代码示例,但在这里,我们使用 Facade 来简化用于监听跨浏览器事件的接口。为此,创建一个可以用于某些代码的通用方法,该代码的任务是检查特性的存在,以便能够提供一个安全的、跨浏览器的兼容解决方案。

1
2
3
4
5
6
7
8
9
var addMyEvent = function(el, ev, fn) {
if(el.addEventListener) {
el.addEventListener(ev, fn, false)''
} else if(el.attachEvent) {
el.attachEvent("on" + ev, fn);
}else {
el['on' + ev] = fn;
}
};

我们都很熟悉的jQuery的$(document).ready(..), 采用了类似的方式。在内部,它实际上是使用了一个被称为bindReady()的方法,它是这样做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// jQuery 1.72
bindReady: function() {
...
if(document.addEventListener){
//使用便利的事件回调
document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);

//可靠的window.onload始终可用
window.addEventListener("load", jQuery.ready, false);
} else if ( document.attachEvent){ //如果是IE事件模型
document.attachEvent("onreadystatechange", DOMContentLoaded);

// 可靠的window.onload 始终可用
window.attachEvent("onload", jQuery.ready);
}
...
}

DOMContentLoaded 事件

1
2
3
4
5
6
7
8
9
DOMContentLoaded不同的浏览器对其支持不同,所以在实现的时候我们需要做不同浏览器的兼容。

1)支持DOMContentLoaded事件的,就使用DOMContentLoaded事件;

2)IE6、IE7不支持DOMContentLoaded,但它支持onreadystatechange事件,该事件的目的是提供与文档或元素的加载状态有关的信息。

3)更低的ie还有个特有的方法doScroll, 通过间隔调用:document.documentElement.doScroll("left");

可以检测DOM是否加载完成。 当页面未加载完成时,该方法会报错,直到doScroll不再报错时,就代表DOM加载完成了。该方法更接近DOMContentLoaded的实现。

Facade不是必须单独使用。它们也可以与其他模式集成,如Module模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  var module = (function() {
var _private = {
i: 5,
get: function() {
console.log("current value:" + this.i);
},
set: function(val) {
this.i = val;
},
run: function() {
console.log("running");
},
jump: function() {
console.log("jumping")
}
};

return {
facade: function(args) {
_private.set(args.val);
_private.get();
if(args.run) {
_private.run();
}
}
};
})()

module.facade({
run: true,
val: 20,
})

在这个示例中,调用module.facade()实际上会在该模块中触发一系列的私有行为,但用户不会接触到。我们让facade变成一个不需要关注实现细节,而且更容易使用的一个特性。

当使用Facade模式时,要试着了解涉及的任何性能成本,并确认是否值得抽象。