常用的javascript设计模式
- 什么是设计模式
 - 单体模式
 - 工厂模式
 - 单例模式
 - 观察者模式(发布订阅模式)
 - 策略模式
 - 模板模式
 - 代理模式
 - 外观模式
 
单体模式:
代码封装在一个起来,只是暴露一个入口,从而避免全部变量的污染1
2
3
4
5
6
7
8
9/*Basic Singleton*/
var Singleton = {
    attribute:true,
    method1:function(){},
   method2:function(){}
};
工厂模式:
提供创建对象的接口,意思就是根据领导(调用者)的指示(参数),生产相应的产品(对象)
流程==》 先设计一个抽象类,这个类不能被实例化,只能用来派生子类,最后通过对子类的扩展实现工厂方法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
32var XMLHttpFactory =function(){};      //这是一个抽象工厂模式
XMLHttpFactory.prototype = {
  //如果真的要调用这个方法会抛出一个错误,它不能被实例化,只能用来派生子类
  createFactory:function(){
    throw new Error('This is an abstract class');
  }
}
var XHRHandler =function(){}; //定义一个子类
// 子类继承父类原型方法
extend( XHRHandler , XMLHttpFactory );
XHRHandler.prototype =new XMLHttpFactory(); //把超类原型引用传递给子类,实现继承
XHRHandler.prototype.constructor = XHRHandler; //重置子类原型的构造器为子类自身
//重新定义createFactory 方法
XHRHandler.prototype.createFactory =function(){
  var XMLHttp =null;
  if (window.XMLHttpRequest){
    XMLHttp =new XMLHttpRequest();
  }else if (window.ActiveXObject){
    XMLHttp =new ActiveXObject("Microsoft.XMLHTTP")
  }
  return XMLHttp;
}
单例模式
如果存在直接返回,如果不存在就创建了再返回
有一些对象我们往往只需要一个,比如全局缓存、浏览器的window对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var single = (function(){
    var unique;
    function getInstance(){
    // 如果该实例存在,则直接返回,否则就对其实例化
        if( unique === undefined ){
            unique = new Construct();
        }
        return unique;
    }
    function Construct(){
        // ... 生成单例的构造函数的代码
    }
    return {
        getInstance : getInstance
    }
})();
观察者模式(发布订阅模式)
一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92var pubsub = {};   // 定义发布者
(function (q) {
    var list = [],  //回调函数存放的数组,也就是记录有多少人订阅了我们东西
        subUid = -1;
    // 发布消息,遍历订阅者
    q.publish = function (type, content) {
        // type 为文章类型,content为文章内容
        
        // 如果没有人订阅,直接返回
        if (!list[type]) {
            return false;
        }
        setTimeout(function () {
            var subscribers = list[type],
                len = subscribers ? subscribers.length : 0;
            while (len--) {
                // 将内容注入到订阅者那里
                subscribers[len].func(type, content);
            }
        }, 0);
        return true;
    };
    //订阅方法,由订阅者来执行
    q.subscribe = function (type, func) {
        // 如果之前没有订阅过
        if (!list[type]) {
            list[type] = [];
        }
        // token相当于订阅者的id,这样的话如果退订,我们就可以针对它来知道是谁退订了。
        var token = (++subUid).toString();
        // 每订阅一个,就把它存入到我们的数组中去
        list[type].push({
            token: token,
            func: func
        });
        return token;
    };
    //退订方法
    q.unsubscribe = function (token) {
        for (var m in list) {
            if (list[m]) {
                for (var i = 0, j = list[m].length; i < j; i++) {
                    if (list[m][i].token === token) {
                        list[m].splice(i, 1);
                        return token;
                    }
                }
            }
        }
        return false;
    };
} (pubsub));
//将订阅赋值给一个变量,以便退订
var girlA = pubsub.subscribe('js类的文章', function (type, content) {
    console.log('girlA订阅的'+type + ": 内容内容为:" + content);
});
var girlB = pubsub.subscribe('js类的文章', function (type, content) {
    console.log('girlB订阅的'+type + ": 内容内容为:" + content);
});
var girlC = pubsub.subscribe('js类的文章', function (type, content) {
    console.log('girlC订阅的'+type + ": 内容内容为:" + content);
});
//发布通知
pubsub.publish('js类的文章', '关于js的内容');  
// 输出:
// girlC订阅的js类的文章: 内容内容为:关于js的内容
// test3.html:78 girlB订阅的js类的文章: 内容内容为:关于js的内容
// test3.html:75 girlA订阅的js类的文章: 内容内容为:关于js的内容
//girlA退订了关于js类的文章 
setTimeout(function () {
    pubsub.unsubscribe(girlA);
}, 0);
//再发布一次,验证一下是否还能够输出信息
pubsub.publish('js类的文章', "关于js的第二篇文章");
// 输出:
// girlB订阅的js类的文章: 内容内容为:关于js的第二篇文章
// girlC订阅的js类的文章: 内容内容为:关于js的第二篇文章
策略模式
就是以前要很多判断的写法,现在把判断里面的内容抽离开来,变成一个个小的个体
策略模式最实用的场合就是某个“类”中包含有大量的条件性语句,比如if…else 或者 switch。每一个条件分支都会引起该“类”的特定行为以不同的方式作出改变。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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53// 对于vip客户
function vipPrice() {
    this.discount = 0.5;
}
 
vipPrice.prototype.getPrice = function(price) {
  return price * this.discount;
}
// 对于老客户
function oldPrice() {
    this.discount = 0.3;
}
 
oldPrice.prototype.getPrice = function(price) {
    return price * this.discount;
}
// 对于普通客户
function Price() {
    this.discount = 1;
}
 
Price.prototype.getPrice = function(price) {
    return price ;
}
// 上下文,对于客户端的使用
function Context() {
    this.name = '';
    this.strategy = null;
    this.price = 0;
}
 
Context.prototype.set = function(name, strategy, price) {
    this.name = name;
    this.strategy = strategy;
    this.price = price;
}
Context.prototype.getResult = function() {
    console.log(this.name + ' 的结账价为: ' + this.strategy.getPrice(this.price));
}
var context = new Context();
var vip = new vipPrice();
context.set ('vip客户', vip, 200);
context.getResult();   // vip客户 的结账价为: 100
var old = new oldPrice();
context.set ('老客户', old, 200);
context.getResult();  // 老客户 的结账价为: 60
var Price = new Price();
context.set ('普通客户', Price, 200);
context.getResult();  // 普通客户 的结账价为: 200
模板模式
就是将一些公共方法封装到父类,子类可以继承这个父类,并且可以在子类中重写父类的方法,从而实现自己的业务逻辑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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49var Interview = function(){};
// 笔试
Interview.prototype.writtenTest = function(){
    console.log("这里是前端笔试题");
};
// 技术面试
Interview.prototype.technicalInterview = function(){
    console.log("这里是技术面试");
}; 
// 领导面试
Interview.prototype.leader = function(){
    console.log("领导面试");
};
// 领导面试
Interview.prototype.HR = function(){
    console.log("HR面试");
};
// 等通知
Interview.prototype.waitNotice = function(){
    console.log("等通知啊,不知道过了没有哦");
};
// 代码初始化
Interview.prototype.init = function(){
    this.writtenTest();
    this.technicalInterview();
    this.leader();
    this.HR();
    this.waitNotice();
};
// 阿里巴巴的笔试和技术面不同,重写父类方法,其他继承父类方法。
var AliInterview = function(){};
AliInterview.prototype = new Interview();
// 子类重写方法 实现自己的业务逻辑
AliInterview.prototype.writtenTest = function(){
    console.log("阿里的技术题就是难啊");
}
AliInterview.prototype.technicalInterview = function(){
    console.log("阿里的技术面就是叼啊");
}
var AliInterview = new AliInterview();
AliInterview.init();
// 阿里的技术题就是难啊
// 阿里的技术面就是叼啊
// 领导面试
// HR面试
// 等通知啊,不知道过了没有哦
代理模式
就是帮别人做事,javascript的解释为:把对一个对象的访问, 交给另一个代理对象来操作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// 补打卡事件
var fillOut = function (lateDate) {
    this.lateDate = lateDate;
};
// 这是bigBoss
var bigBoss = function (fillOut) {
    this.state = function (isSuccess) {
        console.log("忘记打卡的日期为:" + fillOut.lateDate + ", 补打卡状态:" + isSuccess);
    }
};
// 助理代理大boss 完成补打卡审批
var proxyAssis = function (fillOut) {
    
    this.state = function (isSuccess) {
        (new bigBoss(fillOut)).state(isSuccess); // 替bigBoss审批
    }
};
// 调用方法:
var proxyAssis = new proxyAssis(new fillOut("2016-9-11"));
proxyAssis.state("补打卡成功");
// 忘记打卡的日期为:2016-9-11, 补打卡状态:补打卡成功
1  | //图片懒加载  | 
外观模式
说白了,外观模式就是一个函数,封装了复杂的操作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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54function ajaxCall(type,url,callback,data){
    // 根据当前浏览器获取对ajax连接对象的引用
    var xhr=(function(){
        try {
            // 所有现代浏览器所使用的标准方法
            return new XMLHttpRequest();
        }catch(e){}
        
        // 较老版本的internet Explorer兼容
        try{
            return new ActiveXObject("Msxml2.XMLHTTP.6.0");
        }catch(e){}
        try{
            return new ActiveXObject("Msxml2.XMLHTTP.3.0");
            
        }catch(e){}
        try{
            return new ActiveXObject("Microsoft.XMLHTTP");
            
        }catch(e){}
        // 如果没能找到相关的ajax连接对象,则跑出一个错误。
        throw new Error("Ajax not support in this browser.")
    }()),
    STATE_LOADED=4,
    STATUS_OK=200;
    // 一但从服务器收到表示成功的相应消息,则执行所给定的回调方法
    xhr.onreadystatechange=function{
        if(xhr.readyState !==STATE_LOADED){
            return;
        }
        if(xhr.state==STATUS_OK){
            callback(xhr.responseText);
        }
    }
    // 使用浏览器的ajax连接对象来向所给定的URL发出相关的调用
    xhr.open(type.toUpperCase(),url);
    xhr.send(data);
}
// 使用方法
ajaxCall("get","/user/12345",function(rs){
    alert('收到的数据为:'+rs);
})