1. 单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Singleton {
constructor(name) {
this.name = name
this.instance = null
}
getName() {
alert(this.name)
}
static getInstance(name) {
if (!this.instance) {
this.instance = new Singleton(name)
}
return this.instance
}
}

2. 代理模式

虚拟代理:使用proxySynchronousFile来收集一段时间内的请求,最后一次性发送给服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 将请求发送给服务器
synchronousFile (id){
console.log('开始同步文件,id为:'+id);
}
//收集请求的代理并一次性发送
var proxySynchronousFile=function(){
var cache=[];
var timer;
return function(id){
cache.push(id);
if(timer) return;
timer=setTimeout(function(){
synchronousFile(cache.join(','));
clearTimeout(timer);
timer=null;
cache.length=0;
}, 5000);
}
};

缓存代理:

1
2
3
4
5
6
7
8
9
10
var createProxyFactory=function(fn){
var cache={};
return function () {
var args=Array.prototype.join.call(arguments, ',');
if(args in cache){
return cache[args];
}
return cache[args] = fn.apply(this, arguments)
}
}

createProxyFactory是个代理工厂,可以为各种计算方法创建缓存代理。

3. 发布订阅模式

Event类似一个中介者的身份,将订阅者和发布者联系起来。

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
class Event{
constructor(){
this.clientList={};
}
subscribe(key, cb) {
this.clientList[key] = this.clientList[key] || [];
this.clientList[key].push(cb);
}
trigger(key, data) {
if(!this.clientList[key] || this.clientList[key].length === 0) {
return false;
}
this.clientList[key].forEach((cb) => {
cb.call(this, data);
});
}
unsubscribe(key, cb) {
if(key && cb) {
var ins = this.clientList[key].findIndex((item => {
return Object.is(item, cb);
}));
if(ins===-1) return false;
this.clientList[key].splice(ins, 1);
} else if(key) {
this.clientList[key].length = 0;
} else {
return false;
}
}
}

4. 组合模式

组合模式可以让我们使用树形方式创建对象结构,将相同的操作应用在组合对象和单个对象上。从而忽略组合对象和单个对象的差别,从而用一致的方式来处理它们。
组合模式要求组合对象和叶对象拥有相同的接口,而且还需要对一组叶对象的操作具有一致性。并且需要叶节点对于组合节点是一对一的关系,如果映射关系复杂,就需要引入中介者模式来管理这些对象。可以运用职责链模式来提高组合性能。

5. 装饰者模式

装饰者模式在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责。它是继承关系的一个替代方案,在不改变接口的前提下,增强类的性能。
装饰者模式经常会用在框架开发中,我们会希望框架中的函数提供的是一些稳定而方便移植的功能,那些个性化的功能可以在框架之外动态装饰上去。
当不考虑向Function的prototype中添加函数时,我们在Function的prototype中添加before和after函数,这两个函数的功能是将原本函数包围一层,返回一个新的函数,这个新函数的作用是保持上下文不变的执行原有函数和先行或后行执行函数。这个先行函数和后行函数就是需要使用装饰器添加的功能,先行函数实在原函数执行前执行,后行函数是在原函数执行后执行。
先来定义这两个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype.before=function(beforeFn){
var _self=this;//_self保存调用before的函数
return function (){
beforeFn.apply(this, arguments);
return _self.apply(this, arguments); // 返回原函数的执行结果
}
}
Function.prototype.after=function(afterFn){
var _self=this;//_self保存调用before的函数
return function (){
let ret = _self.apply(this, arguments);
afterFn.apply(this, arguments);
return ret; // 返回原函数的执行结果
}
}

下面来举一个具体的例子来展示装饰器的功能,有一个button,点击button需要实现两个功能,一是打开模态框,二是写入日志,将写入日志以装饰器的模式来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<button tag="login" id="button"></button>
<script>
var showLogin = function(){
console.log( '打开模态框' );
}
var log = function(){
console.log( ' 写入日志: ' + this.getAttribute( 'tag' ));
}
showLogin = showLogin.after( log ); // 在打开模态框后写入吗日志
document.getElementById( 'button' ).onclick = showLogin;
</script>
</html>

使用装饰器模式的场景有面向AOP编程。就是支持日志系统、安全检查、调试、缓存等。
在es7中也有装饰器模式,decorator 允许我们在不修改函数内部代码的情况下添加一些额外的行为。在以前的标准中,我们可能会需要通过类似于柯里化(Currying)的方法来实现这个行为:

1
2
3
4
5
6
7
8
9
10
11
function decorate(fn) {
return function () {
// 额外行为
fn.call(this, [].slice.call(arguments));
}
}
function foobar() {
// do something
}
newFoobar = decorate(foobar);
newfoobar(parameters);

但是,有了decorator,我们可以更加简洁地实现这一行为。与此同时,decorator还允许我们对一个class进行修饰,如很常用的就是添加某些属性、某些属性设为只读特性。按其语法,decorator函数接受三个参数:target, key, descriptor。还记得我们可以通过 Object.defineProperty 定义对象的成员吗:

1
2
3
4
5
6
Object.defineProperty(Class.Prototype, 'method', {
value: function () {},
enumerable: false,
configurable: true,
writable: true
});

decorator里的参数与defineProperty的参数一致。实现一个readonly的decorator可以简单地使用下面方式:

1
2
3
4
5
6
7
8
function readonly(target, key, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Class {
@readonly
method () {}
}

当JS引擎执行到decorator的 @ 标志时,它实际执行的代码为:

1
2
3
4
5
6
7
8
let descriptor = {
value: method,
enumerable: false,
configurable: true,
writable: true
};
descriptor = readonly(Class.prototype, 'method', descriptor) || descriptor;
Object.defineProperty(Class.prototype, 'method', descriptor);

因此decorator可以作用在类的原型函数上,decorator还可以作用在class上
下面来看一下怎样对一个类进行decorate。很简单,我们只要操作 decorate 函数参数中的target即可:

1
2
3
4
5
6
7
8
9
10
function className(clsName) {
return function (target) {
target.className = clsName;
}
}
@className('ui.component')
class UIComponent {
//
}
console.log(UIComponent.className);

参考:
http://taobaofed.org/blog/2015/11/16/es7-decorator/