深入理解javascript之设计模式
设计模式
设计模式是命名、抽象和识别对可重用的面向对象设计实用的的通用设计结构。
设计模式确定类和他们的实体、他们的角色和协作、还有他们的责任分配。
每个设计模式都聚焦于一个面向对象的设计难题或问题。
它描写叙述了在其他设计的约束下它是否能使用。使用它后的后果和得失。
由于我们必须终于实现我们的设计模式,所以每个设计模式都提供了样例,代码来对实现进行阐释.
尽管设计模式被描写叙述为面向对象的设计,它们基于那些已经被主流面向对象语言实现过的解决方式...”。
种类
设计模式能够被分成几个不同的种类。
在这个部分我们将分为三类:创建型设计模式、结构设计模式、行为设计模式。
创建型设计模式
创建型设计模式关注于对象创建的机制方法,通过该方法,对象以适应工作环境的方式被创建。主要的对象创建方法可能会给项目添加额外的复杂性,而这些模式的目的就是为了通过控制创建过程解决问题。
属于这一类的一些模式是:构造器模式(Constructor),工厂模式(Factory),抽象工厂模式(Abstract),原型模式(Prototype),单例模式(Singleton)以及 建造者模式(Builder)。
结构设计模式
结构模式关注于对象组成和通常识别的方式实现不同对象之间的关系。该模式有助于在系统的某一部分发生改变的时候,整个系统结构不须要改变。该模式相同有助于对系统中某部分没有达到某一目的的部分进行重组。
在该分类下的模式有:装饰模式,外观模式,享元模式。适配器模式和代理模式。
行为设计模式
行为模式关注改善或精简在系统中不同对象间通信。
行为模式包含:迭代模式。中介者模式,观察者模式和訪问者模式。
以下我们通过分开介绍各个经常使用的设计模式,来加深对设计模式的理解。
构造器模式
构造器是一个当新建对象的内存被分配后,用来初始化该对象的一个特殊函数。对象构造器是被用来创建特殊类型的对象的,首先它要准备使用的对象。其次在对象初次被创建时,通过接收參数。构造器要用来对成员的属性和方法进行赋值。
因为javascript不支持类的概念,所以必须通过构造器来使用newkeyword初始化对象。
一个基础的构造器代码例如以下:
function Car( model, year, miles ) {
this.model = model;
this.year = year;
this.miles = miles;
this.toString = function () {
return this.model + " has done " + this.miles + " miles";
};
}
// 使用:
// 我们能够演示样例化一个Car
var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );
// 打开浏览器控制台查看这些对象toString()方法的输出值
// output of the toString() method being called on
// these objects
console.log( civic.toString() );
console.log( mondeo.toString() );
可是这种话,继承起来比較麻烦,并且每一个Car构造函数创建的对象中,toString之类的函数都会被又一次定义。所以还是要利用原型,来实现最佳的构造器:
function Car( model, year, miles ) {
this.model = model;
this.year = year;
this.miles = miles;
}
// 注意这里我们使用Note here that we are using Object.prototype.newMethod 而不是
// Object.prototype 。以避免我们又一次定义原型对象
Car.prototype.toString = function () {
return this.model + " has done " + this.miles + " miles";
};
// 使用:
var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );
console.log( civic.toString() );
console.log( mondeo.toString() );
工厂模式
一个工厂能提供一个创建对象的公共接口。我们能够在当中指定我们希望被创建的工厂对象的类型。说的简单点。就像饮水机,要咖啡还是牛奶取决于你按哪个button。
简单工厂模式在创建ajax对象的时候能够体现出来,能够通过jquery中的$.ajax方法来理解,也能够通过我自己写的一个ajax方法来理解。地址在:http://runjs.cn/code/j5dkikwu
ajax("test002.txt",{
type:"GET",
data:{
name:"liuf",
age:23
},
onsuccess:function(responseText,xhr){
document.getElementById("input").value=responseText;
},
onfail:function(){
//document.write("fail");
}
});
ajax实际上就是一个工厂方法,至于究竟是用get方法还是post方法,都由后面的代码来决定。这就是前面所说的“一个工厂能提供一个创建对象的公共接口,我们能够在当中指定我们希望被创建的工厂对象的类型”。
单例模式
单例模式之所以这么叫。是由于它限制一个类仅仅能有一个实例化对象。经典的实现方式是。创建一个类,这个类包括一个方法,这种方法在没有对象存在的情况下,将会创建一个新的实例对象。
假设对象存在,这种方法仅仅是返回这个对象的引用。可是javascript本来就是无类的,所以简单地来说。就是没有就创建,有就不创建直接用。
那么我们看看现实中的案例吧。点击一个button后出现一个遮罩层,这是一个经常使用的需求吧。代码例如以下:
var createMask = function(){
return document,body.appendChild( document.createElement(div) );
}
$( 'button' ).click( function(){
var mask = createMask();
mask.show();
})
这样写就会出现一个问题,就是每次调用createMask都会创建一个新的div,尽管能够在隐藏遮罩层时将其remove,可是这样还是会带来性能的损耗。那么能够做例如以下改进。就是在页面一開始就创建div。代码例如以下:
var mask = document.body.appendChild( document.createElement( ''div' ) );
$( ''button' ).click( function(){
mask.show();
} )
这样确实能够保证页面仅仅会创建一个遮罩层,可是也有一个问题,就是假设用户不须要用到这个div,岂不是白白创建了它。于是我们能够借助一个变量来推断是否已经创建过div,代码例如以下:
var mask;
var createMask = function(){
if ( mask ) return mask;
else{
mask = document,body.appendChild( document.createElement(div) );
return mask;
}
}
这样看起来不错,可是mask作为一个全局变量。是否会造成污染呢?所以最好的办法例如以下:
var createMask = function(){
var mask;
return function(){
return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
}
}()
这就是前面所说的“没有就创建,有就不创建直接用”。没错,单例模式就是这么简单,设计模式事实上并不难。编程中我们事实上一直实用到,仅仅是自己没有发现罢了。
桥接模式
桥接模式就是将实现部分和抽象部分分离开来。以便两者能够独立的变化。在实现api的时候,桥接模式很经常使用。
我们以javascript的forEach方法为例:
forEach = function( ary, fn ){
for ( var i = 0, l = ary.length; i < l; i++ ){
var c = ary[ i ];
if ( fn.call( c, i, c ) === false ){
return false;
}
}
}
能够看到,forEach函数并不关心fn里面的详细实现. fn里面的逻辑也不会被forEach函数的改写影响.
使用代码例如以下:
forEach( [1,2,3], function( i, n ){
alert ( n*2 )
} )
forEach( [1,2,3], function( i, n ){
alert ( n*3 )
} )
外观模式
外观模式是一种无处不在的模式,外观模式提供一个高层接口,这个接口使得client或者子系统调用起来更加方法。
比方:
var getName = function(){
return ''svenzeng"
}
var getSex = function(){
return 'man'
}
如今我要调用两个方法,我就能够使用一个更加高层的接口来调用:
var getUserInfo = function(){
var info = getName () + getSex ();
return info;
}
这样就方便组装,假设一開始就把两个写到一个函数中,那就不可以仅仅单独调用当中一个了。
享元模式
享元模式是一个优化反复、缓慢和低效数据共享代码的经典结构化解决方式。它的目标是以相关对象尽可能多的共享数据,来降低应用程序中内存的使用(比如:应用程序的配置、状态等)。通俗的讲,享元模式就是用来降低程序所需的对象个数。
举一个样例,网页中的瀑布流。或者webqq的好友列表中。每次往下拉时。都会创建新的div。那么假设有非常对div呢?浏览器岂不是卡死了?所以我们会想到一种办法。就是把已经消失在视线外的div都删除掉。这样页面就能够保持一定数量的节点,可是频繁的删除和加入节点,又会带来非常大的性能开销。
这个时候就能够用到享元模式了,享元模式能够提供一些共享的对象以便反复利用。比方页面中仅仅能显示10个div,那始终出如今用户视线中的这10个div就能够写成享元。
原理事实上非常easy, 把刚隐藏起来的div放到一个数组中, 当须要div的时候, 先从该数组中取, 假设数组中已经没有了, 再又一次创建一个. 这个数组里的div就是享元, 它们每个都能够当作不论什么用户信息的载体.代码例如以下:
var getDiv = (function(){
var created = [];
var create = function(){
return document.body.appendChild( document.createElement( 'div' ) );
}
var get = function(){
if ( created.length ){
return created.shift();
}else{
return create();
}
}
/* 一个如果的事件,用来监听刚消失在视线外的div,实际上能够通过监听滚动栏位置来实现 */
userInfoContainer.disappear(function( div ){
created.push( div );
})
})()
var div = getDiv();
div.innerHTML = "${userinfo}";
适配器模式
适配器模式就是将一个类的接口转换成客户希望的另外一个接口。通俗一点的说。将像苹果手机不能差在电脑机箱上,必须有一个转换器。而这个转换器就是适配器。
在程序里适配器模式也经经常使用来适配2个接口, 比方你如今正在用一个自己定义的js库. 里面有个依据id获取节点的方法$id(). 有天你认为jquery里的$实现得更酷, 但你又不想让你的project师去学习新的库和语法. 那一个适配器就能让你完毕这件事情.
$id = function( id ){
return jQuery( '#' + id )[0];
}
这样就不用再一个一个的改动了。
代理模式
代理模式就是把对一个对象的訪问,交给还有一个代理对象来操作。
说得通俗一点,程序猿每天写日报。日报最后会给总监批阅。可是假设全部人都直接发给总监。那总监就没法工作了。
所以每一个人会把自己的日报发给自己的组长,再由组长转发给总监。这个组长就是代理。
编程中用到代理模式的情况也不少,比方大量操作dom时,我们会先创建文档碎片,再统一加到dom树中。
示比例如以下:
中介者模式
中介者模式是观察者模式中的共享被观察者对象。
在这个系统中的对象之间直接的公布/订阅关系被牺牲掉了。取而代之的是维护一个通信的中心节点。中介者模式和代理模式是有差别的。差别例如以下:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWV2aWNreQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
中介者对象能够让各个对象之间不须要相互引用。从而使其耦合松散,并且能够独立的改变透明之间的交互。
通俗点讲,银行在存款人和贷款人之间也能看成一个中介。存款人A并不关心他的钱最后被谁借走。
贷款人B也不关心他借来的钱来自谁的存款。由于有中介的存在。这场交易才变得如此方便。
在编程中,大名鼎鼎的MVC结构中的Controller不就是一个中介者吗?拿backbone举例. 一个mode里的数据并不确定最后被哪些view使用. view须要的数据也能够来自随意一个mode. 全部的绑定关系都是在controler里决定. 中介者把复杂的多对多关系, 变成了2个相对简单的1对多关系。
演示样例代码例如以下:
var mode1 = Mode.create(), mode2 = Mode.create();
var view1 = View.create(), view2 = View.create();
var controler1 = Controler.create( mode1, view1, function(){
view1.el.find( ''div' ).bind( ''click', function(){
this.innerHTML = mode1.find( 'data' );
} )
})
var controler2 = Controler.create( mode2 view2, function(){
view1.el.find( ''div' ).bind( ''click', function(){
this.innerHTML = mode2.find( 'data' );
} )
})
观察者模式
观察者模式是这样一种设计模式。
一个被称作被观察者的对象,维护一组被称为观察者的对象,这些对象依赖于被观察者,被观察者自己主动将自身的状态的不论什么变化通知给它们。
当一个被观察者须要将一些变化通知给观察者的时候,它将採用广播的方式,这条广播可能包括特定于这条通知的一些数据。
当特定的观察者不再须要接受来自于它所注冊的被观察者的通知的时候,被观察者能够将其从所维护的组中删除。
通俗点理解,就是面试官是被观察者。而等待通知的人是观察者。
javascript中平时接触的dom事件。事实上就是一种观察者模式的体现:
div.onclick = function click (){
alert ( ''click' )
}
仅仅要订阅了div的click事件,当点击div的时候,click函数就会运行。
观察者模式能够非常好的实现连个模块之间的解耦,假如一个多人合作的项目,我负责Map和Gamer模式:
loadImage( imgAry, function(){
Map.init();
Gamer.init();
} )
<p>而别人负责loadImage方法的维护。假设有一天,我要加上一个Sound模块,而我无权动用别人的代码。仅仅能空空等待别人回来加入:</p><pre name="code" class="html">loadImage( imgAry, function(){
Map.init();
Gamer.init();
Sount.init();
} )
所以我们能够做例如以下修改:
loadImage.listen( ''ready', function(){
Map.init();
})
loadImage.listen( ''ready', function(){
Gamer.init();
})
loadImage.listen( ''ready', function(){
Sount.init();
})
loadImage完毕之后。不用再关心未来会发送什么,接下来它仅仅须要发送一个信号:
loadImage.trigger( ‘ready’ );
全部监听ready事件的对象就都会收到通知。这就是观察者模式的应用场景。
讲到这里。经常使用的设计模式也讲完了,深入理解javascript系列也将告一段落。
深入理解javascript之设计模式的更多相关文章
- 深入理解JavaScript的设计模式
使用适当的设计模式可以帮助你编写更好.更易于理解的代码.这样的代码也更容易维护.但是,重要的是不要过度使用它们.在使用设计模式之前,你应该仔细考虑你的问题是否符合设计模式. 当你开始一个新的项目时,你 ...
- js架构设计模式——理解javascript中的MVVM开发模式
理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewMod ...
- 深入理解JavaScript系列(38):设计模式之职责链模式
介绍 职责链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象 ...
- 深入理解JavaScript系列(36):设计模式之中介者模式
介绍 中介者模式(Mediator),用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 主要内容来自:http://www ...
- 深入理解JavaScript系列(30):设计模式之外观模式
介绍 外观模式(Facade)为子系统中的一组接口提供了一个一致的界面,此模块定义了一个高层接口,这个接口值得这一子系统更加容易使用. 正文 外观模式不仅简化类中的接口,而且对接口与调用者也进行了解耦 ...
- 深入理解JavaScript系列(31):设计模式之代理模式
介绍 代理,顾名思义就是帮助别人做事,GoF对代理模式的定义如下: 代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. 代理模式使得代理对象控制具体对象的引用.代理几乎可以是任何对 ...
- 深入理解JavaScript系列(25):设计模式之单例模式
介绍 从本章开始,我们会逐步介绍在JavaScript里使用的各种设计模式实现,在这里我不会过多地介绍模式本身的理论,而只会关注实现.OK,正式开始. 在传统开发工程师眼里,单例就是保证一个类只有一个 ...
- 深入理解JavaScript系列(33):设计模式之策略模式(转)
介绍 策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户. 正文 在理解策略模式之前,我们先来一个例子,一般情况下,如果我们要做数据合法性验证,很 ...
- 深入理解JavaScript系列(44):设计模式之桥接模式
介绍 桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化. 正文 桥接模式最常用在事件监控上,先看一段代码: addEvent(element, 'click', getBe ...
随机推荐
- 【图论 动态规划拆点】luoguP3953 逛公园
经典的动态规划拆点问题. 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 NN 个点 MM 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口, NN 号点是公园的出口,每条边有一个非负 ...
- docker 安装 openresty
文章来源: 1.拉取镜像 # docker pull openresty/openresty 2.启动openresty # docker run -it --name openresty -p : ...
- Spring Cloud Stream在同一通道根据消息内容分发不同的消费逻辑
应用场景 有的时候,我们对于同一通道中的消息处理,会通过判断头信息或者消息内容来做一些差异化处理,比如:可能在消息头信息中带入消息版本号,然后通过if判断来执行不同的处理逻辑,其代码结构可能是这样的: ...
- 学习Python第一天,命令很多跟Linux还有脚本语言相似。
学习Python第二天,看了一天,有点头疼,准备先休息一会,再继续.有一点C语言和Java基础,学起来不是很费劲.学习热情尚好. 学习了dir,math模块,import加载模块,有跟Linux相似的 ...
- LeetCode 653. Two Sum IV – Input is a BST
Given a Binary Search Tree and a target number, return true if there exist two elements in the BST s ...
- Uiautomator简介及其环境搭建、测试执行
UiAutomator框架使用指南 UiAutomator是Google开发的自动化测试工具,通过UI创建自动化测试代码,来测试界面(UI)的有效功能,可以针对应用程序运行在一个或更多的设备上.我们并 ...
- [android开发篇]自定义权限
有时候,我们可能遇到如下需求场景:当用户在一个应用程序中进行某项操作时,会启动另外一个应用程序,最常见的时直接打开了另外一个应用程序,并进入其中某个Activity(如:有的应用中有推荐应用列表,当用 ...
- 【软考5】解释型 or 编译型
导读:在上篇博客中,我们了解到,目前的编码语言经过不断的发展,已经经历了机器语言--汇编语言--高级语言的过程.虽然我们的编码语言在不停的升级,但作为计算机来说,它始终是一个只能理解0和1构成的机器语 ...
- Web开发细节搜集
App_Data 百度百科: App_Data文件夹应该包含应用程序的本地数据存储.它通常以文件(诸如Microsoft Access或Microsoft SQL Server Express数据库 ...
- 总结搭建Oracle11g DG踩的坑
此次的操作环境是Oracle11g 单实例,os为Linux,采用duplicate在线创建物理备库 primary上设置相关参数 ALTER SYSTEM SET LOG_ARCHIVE_CONFI ...