JS设计模式
什么是模式
模式是一种可复用的解决方案
优点
- 已验证的解决方案
- 模式容易被复用
- 模式富有表达力
模式状态测试、Proto模式及三法则
成为有效模式
适合性、实用性、适用性。
设计模式的结构
一种模式最初是以一种规则的形式呈现的,该规则建立下面这样的关系
* 上下文
* 上下文里产生的元件系统
* 解决元件在上下文中自身问题的配置
设计模式类别
- 创建型设计模式
专注于处理对象创建机制,以适合给定情况的方式来创建对象。
包括:Constructor(构造器)、Factory(工厂)、Abstract(抽象)、Prototype(原型)、Singleton(单例)和Builder(生成器) - 结构型设计模式
结构型设计模式与对象组合有关,通常可以用于找出在不同对象之间建立关系的简单方法。
这种模式有助于确保系统某一部分发生变化时,系统整个结构不需要同时改变。
包括:Decorator(装饰者)、Facade(外观)、Flyweight(享元)、Adapter(适配器)、Proxy(代理) - 行为设计模式
专注于改善或简化系统中不同对象之间的通信
包括:Iterator(迭代器)、Mediator(中介者)、Observer(观察者)、Visitor(访问者)
js设计模式
Constructor模式
对象创建
- ’.‘语法
- 中括号语法
- Object.defineProperty
基本Constructor
带原型的Constructor
such as :
Car.prototype.functionName = function(){}
Module模式
js中有几种用于实现模块的方法:
* 对象字面量表示法
* Module模式
* AMD模块
* CommonJS
* ECMAScript Harmony 模块
对象字面量
Module模式
私有
Module模式使用闭包封装’私有‘状态和组织。提供了一种包装混合公有/私有方法和变量的方式,防止其泄露至全局作用域。
在Module模式内,由于闭包的存在,声明的变量和方法只在该模式内部可用。但在返回对象上定义的变量和方法,则对外部使用者都是可用的。
Module模式模板
var myNamespace =(function(){
//私有计数器变量
var myPrivateVar = 0;
var myPrivateMethod = function(foo){
console.log(foo);
};
return {
myPublicVar :'foo',
myPublicFunction:function(bar){
myPrivateVar++;
myPrivateMethod(bar);
}
};
})();
Module模块变化
引入混入
全局变量可以作为参数传递给模块的匿名函数
引出
优点
缺点
访问公有和私有成员的方式不同,想改变可见性时必须要修改每一个用过该成员的地方。
Singleton单例模式
限制了类的实例化次数只能一次
在该实例不存在的情况下,可以通过一个方法创建一个类来实现创建类的新实例;如果实例已经存在,它会简单返回该对象该对象的引用。
在js中,Singleton充当共享资源命名空间,从全局命名空间中隔离出代码实现,从而为函数提供单一访问点。
var mySingleton =(function(){
//实例保持了singleton的一个引用
var instance;
function init(){
function privateMethod(){
console.log('I am private');
}
var privateVar = 'I am also private';
return {
publicMethod:function(){
console.log("the public can see me");
},
publicProperty:"I am also public"
};
};
return{
getInstance:function(){
if(!instance){
instance = init();
}
return instance;
}
};
})();
Singleton模式适用性的描述如下:
* 当类只能有一个实例而且客户可以从一个众所周知的访问点访问
* 该类的唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
Observer观察者模式
一个或多个观察者对目标的状态感兴趣,它们通过将自己依附在目标对象上以便注册所感兴趣的内容。目标状态发生改变并且观察者可能对这些改变感兴趣,就会发送一个通知消息,调用每个观察者的更新方法。当观察者不再对目标状态感兴趣,他们可以简单地将自己从中分离。
Observer模式和Publish/Subscribe模式的区别
Observer优点
是用于设计解耦性系统的最佳工具之一
Publish/Subscribe实现
ECMAScript实现是由事件驱动的
Publish/Subscribe实现
(例子1)用户界面通知
一个负责显示实时股票信息的Web应用程序
一个显示股票统计的网格和一个显示最后更新点计数器,当数据模型改变时,应用程序需要更新网格和计数器。
目标:数据模型
观察者:网格和计数器
div.onclick = function click(){
alert('click');
}
只要订阅了div的click事件,当点击div的时候,function click就会被触发。
Mediator(中介者)模式
行为设计模式
通过一个中介者来连接两个对象的模式,封装对象之间的交互
MVC模式中的Controller起到了中介者的作用,
var Participant = function(name) {
this.name = name;
this.chatroom = null;
};
Participant.prototype = {
send: function(message, to) {
this.chatroom.send(message, this, to);
},
receive: function(message, from) {
log.add(from.name + " to " + this.name + ": " + message);
}
};
var Chatroom = function() {
var participants = {};
return {
register: function(participant) {
participants[participant.name] = participant;
participant.chatroom = this;
},
send: function(message, from, to) {
if (to) {
to.receive(message, from);
} else {
for (key in participants) {
if (participants[key] !== from) {
participants[key].receive(message, from);
}
}
}
}
};
};
var log = (function() {
var log = "";
return {
add: function(msg) { log += msg + "\n"; },
show: function() { alert(log); log = ""; }
}
})();
function run() {
var yoko = new Participant("Yoko");
var john = new Participant("John");
var paul = new Participant("Paul");
var ringo = new Participant("Ringo");
var chatroom = new Chatroom();
chatroom.register(yoko);
chatroom.register(john);
chatroom.register(paul);
chatroom.register(ringo);
yoko.send("All you need is love.");
yoko.send("I love you John.");
john.send("Hey, no need to broadcast", yoko);
paul.send("Ha, I heard that!");
ringo.send("Paul, what do you think?", paul);
log.show();
}
聊天室起到了中介者的作用。
Prototype(原型)模式
Command(命令)模式
此模式旨在将方法调用、请求或操作封装到单一对象中,从而根据我们不同的请求对客户进行参数化和传递可供执行的方法调用。
将调用操作的对象与知道如何实现该操作的对象解耦,并在交换出具体类方面提供更大的整体灵活性。
Facade(外观)模式
外观模式提供一个高层接口,这个接口使得客户端或子系统更加方便调用。
可以对用户隐藏真正的细节,用户只关心最高层的接口。
例如:
var stopEvent = function(e){
e.stopPropagation();
e.preventDefault();
}
Factory工厂模式
创建型模式,不显式地要求使用一个构造函数。简单工厂模式是由一个方法来决定到底要创建哪个类的实例,而这些实例经常都拥有相同的接口,这种模式主要用在所实例化的类型在编译期并不能确定,而是在执行期决定的情况。
function createObject(name,age,profession){//集中实例化的函数
var obj = new Object();
obj.name = name;
obj.age = age;
obj.profession = profession;
obj.move = function () {
return this.name + ' at ' + this.age + ' engaged in ' + this.profession;
};
return obj;
}
var test1 = createObject('trigkit4',22,'programmer');//第一个实例
var test2 = createObject('mike',25,'engineer');//第二个实例
工厂模式分为 简单工厂、抽象工厂、智能工厂
Factory模式主要在以下场景使用:
* 当对象或组件设计高复杂性时
* 当需要根据所在的不同环境轻松生成对象的不同实例时
* 当处理很多共享相同属性的小型对象或组件时
装饰者模式
装饰者模式用于运行时动态为对象附加功能。
主要解决的问题是:将对象与可扩充的简单基本单元分离出来,按需增强该对象。
通过两种方式:
继承
装饰列表
Flyweight(享元)模式
结构型解决方案,用于优化重复、缓慢及数据共享效率低的代码。旨在通过与相关的对象共享尽可能多的数据来减少应用程序中内存的使用。
两种方式:
第一种用于数据层,处理内存中保存的大量数据
第二种用于DOM层,Flyweight可以用作中央事件管理器,避免将事件处理程序附加到父节点的每个子节点上。
例如:图书馆书籍管理
//书籍内部状态
var Book = function(title,author,pageCount,publisherID,ISBN){
this.title =title;
this.author=author;
this.pageCount = pageCount;
this.publisherID = publisherID;
this.ISBN=ISBN;
};
//基本工厂
var BookFactory = (function(){
var existingBooks={},existingBook;
return {
createBook:function(title,author,pageCount,publisherID,ISBN){
existingBook = existingBooks[ISBN];
if(!!existingBook){
return existingBook;
}else{
var book = new Book(title,author,pageCount,publisherID,IISBN);
existingBooks[ISBN] = book;
return book;
}
}
};
});
//管理外部状态
//书籍记录管理器单例
var BookRecordManager = (function(){
var bookRecordDatabase ={};
return {
//添加新书到图书馆系统
addBookRecord :function(id,title,author,pageCount,publisherID,ISBN,checkoutDate,checkoutMember,dueReturnDate,availability){
var book = BookFactory.createBook(title,author,pageCount,publisherID,ISBN);
bookRecordDatabase[id] ={
checkoutMember:checkoutMember,
checkoutDate:checkoutDate,
dueReturnDate:dueReturnDate,
availability:availability,
book:book
};
},
updateCheckoutStatus :function(bookID,newStatus,checkoutDate,checkoutMember,newReturnDate){
var record = bookRecordDatabase[bookID];
record.availability = newStatus;
record.checkoutDate = checkoutDate;
record.checkoutMember = checkoutMember;
record.dueReturnDate = newReturnDate;
},
extendCheckoutPeriod :function(bookID,newReturnDate){
bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
},
isPastDue:function(bookID){
var currentDate = new Date();
return currentDate.getTime() > Date.parse(bookRecordDatabase[bookID].dueReturnDate)
}
};
});
jQuery中,用户将初始化点击绑定到一个容器div的同时,使用了一个stateManager命名空间来封装我们的享元逻辑。
HTML
<div id="container">
<div class="toggle" href="#">
More Info (Address)
<span class="info">
This is more information
</span>
</div>
<div class="toggle" href="#">
Even More Info (Map)
<span class="info">
<iframe src="http://www.baidu.com"></iframe>
</span>
</div>
</div>
JS
var stateManager ={
fly: function(){
var self = this;
$("#container").unbind().on("click",function(e){
var target = $(e.originalTarget || e.srcElement);
if(target.is("div.toggle")){
self.handleClick(target);
}
});
},
handleClick:function(elem){
elem.find("span").toggle("slow");
}
};
MV*模式
MVC模式
View(视图)是应用程序数据的可视化表示
MVP模式
模型-视图-表示器
Model-View-Presenter
由View进行请求,表示器执行任何与用户请求有关的工作,并将数据回传给它们。
MVVM模式
模型-视图-视图模型
视图是主动视图,有数据绑定
Model:应用程序将会使用的特定领域数据信息
View:与用户进行交互的应用程序的一部分
ViewModel:一个专门的Controller,充当数据转换器。
将Model信息转变为View信息,将命令从View传递到Model。
是一个用于保存用户正在使用且尚未保存数据的层
View 和 ViewModel之间通过数据绑定和事件进行通信。
Model和ViewModel上的属性通过双向数据绑定进行同步和更新。
jquery中的设计模式
Composite组合模式
部分整体模式,将所有对象组合成树形结构,使用户只需要操作最上层接口,就可以对所有成员做相同操作。
Adapter适配器模式
Adapter模式将对象或类的接口interface,转变为与特定的系统兼容的接口。
适配器模式经常用来适配两个接口,比如调用的js库中有一个根据id获取节点的方法
id(),但是jquery里的
()也可以根据id获取节点,可以用一个适配器来修改:
$id = function(id){
return jQuery("#"+id)[0];
Facade外观模式
提供一个高层接口,使得客户端或子系统更加方便调用。
//非外观
var getName =function(){
return 'hahah';
}
var getAge = function(){
return 22;
}
//外观模式
var getInfo = function(){
var info = getName() + getAge();
return info;
}
Iterator迭代器模式
each()函数就是迭代器模式
each:function(callback,args){
return jQuery.each(this,callback,args);
}
proxy代理模式
代理模式就是把一个对象的访问,交给另一个代理对象来操作。
策略模式
定义一系列算法,把它们封装起来,并使他们可以相互替换。
$(div).animate({"left:200px"},1000,'linear'); //匀速运动
$(div).animate({"left:200px"},1000,'cubic'); //三次方缓动
nameInput.addValidata({
notNull:true;
drityWords:true;
maxLength:30;
})
validataList ={
notNull:function(value){
return value !=='';
},
maxLength:function(value){
return value.length() >maxLen;
}
}
备忘录模式
备忘录模式常用于数据缓存
var Page = function(){
var page = 1,
cache = {},
data;
return function( page ){
if ( cache[ page ] ){
data = cache[ page ];
render( data );
}else{
Ajax.send( 'cgi.xx.com/xxx', function( data ){
cache[ page ] = data;
render( data );
})
}
}
}()
职责链模式
职责链模式是一个对象A向另一个对象B发起请求,如果B不处理,可以把请求转给C,如果C不处理,又可以把请求转给D。一直到有一个对象愿意处理这个请求为止。
打个比方,客户让老板写个php程序。老板肯定不写,然后老板交给了部门经理。部门经理不愿意写,又交给项目经理。项目经理不会写,又交给程序员。最后由码农来完成。
在这个假设里, 有几条职责链模式的特点。
1 老板只跟部门经理打交道,部门经理只联系项目经理,项目经理只找码农的麻烦。
2 如果码农也不写,这个项目将会流产。
3 客户并不清楚这个程序最后是由谁写出来的。
js中的事件冒泡就是作为一个职责链来实现的。一个事件在某个节点上被触发,然后向根节点传递, 直到被节点捕获。