JavaScript 设计模式
设计模式
- 可重用、可扩展、可解耦、容易理解
- 目标是为了代码复用,增加可维护性
- 原则:
- 对扩展开放,对修改关闭
- 里氏转换原则:子类继承父类,单独完全可以运行
- 依赖倒转原则:引用一个对象,如果这个对象有底层类型,直接引用底层
- 接口隔离原则:每个接口应该是一种角色
- 合成/聚合复用原则:新的对象应使用一些已有的对象,使之成为新对象的一部分
- 迪米特原则: 一个对象应对其他对象有尽可能少得了解
单例模式
-
一个类只有一个实例
-
实现方法:先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回
-
单例是一个命名空间的提供者
-
唯一、易阻塞、有序、序列化
- 数据
- 执行通道
- 执行体
-
多个线程调用单例
-
作用:
- 模块间通信
- 系统中某个类的对象只能存在一个
- 保护自己的属性和方法
-
注意:
- this的使用,使用instanceof来验证
- 闭包容易造成内存泄漏,不需要的赶快干掉
- new 的成本(继承)
内存泄漏
当一个对象不需要再使用,本该被回收时,受另一个持有它引用的对象影响,导致它不能被回收。
本该被回收的对象不能被回收而停留在堆内存中。
内存泄漏的影响:应用所需的内存超过系统分配的内存限额,内存溢出导致应用Crash
单例模型普通样例: 不同单例在同一命名空间
var demo1= (function(argument){
var attr1 = function(message){
this.menling = message;
};
var men;
var obj={
fun1:function(message){
if(men){
men = new attr1(message);
}
return men;
}
}
return obj;
})();
var demo2 = {
fun2 :function(msg){
var _demo = demo1.fun1(msg);
alert(_demo);
_demo = null; //等待垃圾回收 及时清理存储空间
}
}
demo2.fun2('didi);
把对应的逻辑封装到对应的区块中,不同命名空间下,减少变量污染
//页面上6个按钮 abcdef
//abc在top命名空间下
//def在banner命名空间下
var top={
init:function(){ //top的初始化
this.render();
this.bind();
},
a:4, //传递的参数
render:function(){//把所有DOM元素放进去
var me = this;
me.btna = $('#a');
},
bind :function(){
var me = this;
me.btna.click(function(){
//业务逻辑
me.test();
});
},
test:function(){
a=5;
}
}
var banner={
init:function(){ //top的初始化
this.render();
this.bind();
},
a:4, //传递的参数 不同命名空间互相保护
render:function(){//把所有DOM元素放进去
var me = this;
me.btna = $('#a');
},
bind :function(){
var me = this;
me.btna.click(function(){
//业务逻辑
me.test();
});
},
test:function(){
top.a=5;
}
}
top.init();
banner.init();
构造函数模式 (构建构造函数)
-
用于创建特定类型的对象
- 声明了使用对象
- 接受参数以便第一次创建对象的时候设置对象的成员值
-
自定义自己的构造函数,并声明自定义类型对象的属性和方法
-
用来实现实例
- 用new关键字来调用自定义的构造函数
- 在构造函数内部,this引用的是新创建的对象
- 作用
- 用于创建特定类型的对象
- 第一次声明的时候给对方扶着
- 自己声明构造函数,赋予属性和方法
- 注意事项
- 声明函数的时候处理业务逻辑(只包含特定对象的业务逻辑)
- 与单例区别,配合单例实现初始化
- 构造函数大写字母开头
- 注意new的成本(继承)(公用的方法放在构造函数中)
//js开发写单引号
//js必须通过new来让this指向该对象
//PHP实现构造函数模式,关键字
var AA ={
zaomen:function(huawen){
if(!(this instanceof zaomen)){ //instanceof 用于检测this是否是zaomen类的一个实例
return new zaomen();
}
this.suo = '普通',
this.huawen = huawen || '普通' ,
this.create = function(){
return '锁头' +this.suo +'花纹'+this.huawen;
}
}
}
var BB ={
zaomen:function(huawen){
if(!(this instanceof zaomen)){ //instanceof 用于检测this是否是zaomen类的一个实例
return new zaomen();
}
this.suo = '普通',
this.huawen = huawen || '普通' ,
this.create = function(){
return '锁头' +this.suo +'花纹'+this.huawen;
}
}
}
var xiaozhang = AA.zaomen();
console.log(xiaozhang.create());
var xiaoli = new BB.zaomen('绚丽');
console.log('xiaoli' + xiaoli.create());
建造者模式
-
将一个复杂对象的构建与其表示相分离
- 同样的构建过程,创建不同的表示
-
一个指挥者 + 建造者
-
分步骤构建一个复杂的对象
- 分步骤 :稳定的
- 复杂对象的各个部分 : 经常变化
-
作用:
- 分布创建一个复杂的对象
- 解耦封装过程和具体创建的组件
- 无需关心组件如何组装
-
注意事项:
- 一定要一个稳定明确的算法逻辑进行支持
- 加工工艺是暴露
//发送一个请求 白富美
//$.ajax建造者模式,包工头
//工人 完整的工程
$.ajax({
url:'a.php',
success:function(){
}
})
//建造者模式 :按照人、物清晰的逻辑关系来清晰的设计
function Fangzi(){
this.woshi ='';
this.keting ='';
this.chufang = '';
}
function Baogongtou(){
this.gaifanzi = function(gongren){
gongren.jian_woshi();
gongren.jian_keting();
gongren.jian_chufang();
}
}
function Gongren(){
this.jian_woshi = function(){
console.log('卧室建好了');
}
this.jian_keting = function(){
console.log('客厅建好了');
}
this.jian_chufang = function(){
console.log('厨房建好了');
}
this.jiaogong = function(){
var _fangzi = new Fangzi();
_fangzi.whoshi ='ok';
_fangzi.keting ='ok';
_fangzi.chufang = 'ok';
return _fangzi;
}
}
var gongren = new Gongren();
var baogongtou = new Baogongtou();
var baogongtou.gaifangzi(gongren);
var myfangzi = gongren.jiaogong();
console.log(myfangzi);
工厂模式
-
定义一个类
-
用于创建对象的接口,这个接口由子类决定实例化哪一个类
- 讲一个类的实例化延迟到了子类
- 子类可以重写接口方法:以便创建的时候指定自己的对象类型(抽象工厂)
-
创建对象的流程赋值的时候:依赖于很多设置文件
-
让子类定义需要创建的对象类型
-
作用
- 对象的构建十分复杂
- 需要依赖具体的环境创建不同的实例
- 处理大量具有相同属性的小对象
-
注意事项
- 不能滥用工程,有时会增加复杂度
var gongchang = {};
gongchang.chanyifu = function(){
this.gongren = 50;
alert('我们的底线' +this.gongren);
}
gongchang.chanxie = function(){
alert('产鞋子');
}
gongchang.yunshu = function(){
alert('运输');
}
gongchang.changzhang = function(para){
//js用了构造函数模式 以及 单例模式
return new gongchang[para]();
}
var me = gongchang.changzhang('changyifu');
alert(me.gongren);
让一个工厂分成几个部门,每个部门干自己的事儿 调用时只需要找到这个工厂的这个部分就可以
//简单工厂模式
var XMLHttpFactory = function(){
}
XMLHttpFactory.createXMLHttp = function(){
var XMLHttp = null;
if(window.XMLHttpRequest){
XMLHttp = new XMLHttpRequest();
}elseif(window.ActiveXobject{
XMLHttp = new ActiveXObject('Microsoft.XMLHttp');
}
return XMLHttp;
}
var AjaxHander = function(){
var XMLHttp = XMLHttpFactory.createXMLHttp();
}
抽象工厂是给你个tag,让你知道这个是什么功能,但是你需要重构丰富他的内容才能使用
//抽象工厂 只留一个,但不做具体的事儿
var XMLHttpFactory = function(){
}
XMLHttoFactory.prototype = {
//抛出错误,不能被实例化,只能用来派生子类
createFactory:function(){
throw new Error('This is an abstract class');
}
}
//派生子类
var XHRHandler = function(){
XMLHttpFactory.call(this);
}
//this发生变化
XHRHandler.prototype = new XMLHttpFactory();
XHRHandler.prototype.constructor = XHRHandler;
XHRHandler.prototype.createFactory = function(){
var XMLHttp = null;
if(window.XMLHttpRequest){
XMLHttp = new XMLHttpRequest();
}elseif(window.ActiveXobject{
XMLHttp = new ActiveXObject('Microsoft.XMLHttp');
}
return XMLHttp;
}
代理模式(中间件函数进行其他各方,其它各方是相对独立的两个对象)
-
帮助别人做事
- GoF~四人帮,合作的一本书成为学习设计模式的圣经。
-
代理模式Proxy 为其他对象提供一种代理以控制对这个对象的访问。
-
代理模式使得代理对象控制具体对象的引用
-
代理几乎可以是任何对象:文件、资源、内存中的对象、或者是一些难以复制的东西
-
作用
- 远程代理(一个对象将不同空间的对象进行局部代理)
-
虚拟代理(根据需要创建开销很大的对象)
-
安全代理(控制真实对象的访问权限)
-
智能指引(调用对象代理处理另外一些事情如垃圾回收机制)
-
注意:
- 不能滥用代理
//代理模式需要有三方
//买家
function maijia(){
this.name ='小明';
}
//中介卖房
function zhongjie(){}
zhongjie.prototype.maifang = funcrion(){
new fangdong(new maijia()).maifang('20万');
}
//房东
function fangdong(){
this.maijianame = maijia.name;
this.maifang = function(money){
alert('收到了来自"'+this.maijianame+'"的'+money+'人民币');
}
}
(new zhongjie).maifang();
命令模式 (传参)
-
对方法调用进行参数化处理和传送
- 经过处理过的方法调用可以在任何需要的时候执行
-
将函数的调用、请求、操作封装成一个单一的对象,然后对这个对象进行一系列的处理
-
用来消除调用操作的对象和实现操作的对象之间的耦合
-
作用
- 将函数的封装、请求、调用结合为一体
-
调用具体的函数来解耦命令对象与接收对象
-
提高程序模块化的灵活性
-
注意事项
- 不需要接口一致,直接调用函数就好,以免造成浪费
var lian ={};
lian.paobin = function(pao_num){
alert(pao_num +'炮'+'开始战斗');
}
lian.bubin=function(bubing_num){
alert(bubing_num +'人'+'开始战斗');
}
lian.lianzhang = function(mingling){
lian[mingling.type](mingling.num);
}
//总司令开始发令
lian.lianzhang({
type:'paobing',
mum:100
})
lian.lianzhang({
type:'bubing',
num:500
})