GOF官方定义: 访问者模式是表示一个作用于某个对象结构中的各元素的操作。它使可以在不改变各元素的类的前提下定义作用于这些元素的新操作。我们在使用一些操作对不同的 对象进行处理时,往往会根据不同的对象选择不同的处理方法和过程。在实际的代码过程中,我们可以发现,如果让所有的操作分散到各个对象中,整个系统会变得 难以维护和修改。且增加新的操作通常都要重新编译所有的类。因此,为了解决这个问题,我们可以将每一个类中的相关操作提取出来,包装成一个独立的对象,这 个对象我们就称为访问者(Visitor)。利用访问者,对访问的元素进行某些操作时,只需将此对象作为参数传递给当前访问者,然后,访问者会依据被访问 者的具体信息,进行相关的操作。

据统计,上面这段话只有5%的人会看到最后一句。那么通俗点讲,访问者模式先把一些可复用的行为抽象到一个函数(对象)里,这个函数我们就称为访问 者(Visitor)。如果另外一些对象要调用这个函数,只需要把那些对象当作参数传给这个函数,在js里我们经常通过call或者apply的方式传递 this对象给一个Visitor函数.

访问者模式也被称为GOF总结的23种设计模式中最难理解的一种。不过这有很大一部分原因是因为《设计模式》基于C++和Smalltalk写成. 在强类型语言中需要通过多次重载来实现访问者的接口匹配。

而在js这种基于鸭子类型的语言中,访问者模式几乎是原生的实现, 所以我们可以利用apply和call毫不费力的使用访问者模式,这一小节更关心的是这种模式的思想以及在js引擎中的实现。

我们先来了解一下什么是鸭子类型,说个故事:

很久以前有个皇帝喜欢听鸭子呱呱叫,于是他召集大臣组建一个一千只鸭子的合唱团。大臣把全国的鸭子都抓来了,最后始终还差一只。有天终于来了一只自告奋勇 的鸡,这只鸡说它也会呱呱叫,好吧在这个故事的设定里,它确实会呱呱叫。 后来故事的发展很明显,这只鸡混到了鸭子的合唱团中。— 皇帝只是想听呱呱叫,他才不在乎你是鸭子还是鸡呢。

这个就是鸭子类型的概念,在js这种弱类型语言里,很多方法里都不做对象的类型检测,而是只关心这些对象能做什么。

Array构造器和String构造器的prototype上的方法就被特意设计成了访问者。这些方法不对this的数据类型做任何校验。这也就是为什么arguments能冒充array调用push方法.

看下v8引擎里面Array.prototype.push的代码:

function ArrayPush() {  var n = TO_UINT32( this.length );
var m = %_ArgumentsLength(); for (var i = 0; i < m; i++) { this[i+n] = %_Arguments(i); //属性拷贝 } this.length = n + m; //修正length return this.length;}

可以看到,ArrayPush方法没有对this的类型做任何显示的限制,所以理论上任何对象都可以被传入ArrayPush这个访问者。

不过在代码的执行期,还是会受到一些隐式限制,在上面的例子很容易看出要求:

1、 this对象上面可储存属性. //反例: 值类型的数据

2、 this的length属性可写. //反例: functon对象, function有一个只读的length属性, 表示形参个数.

如果不符合这2条规则的话,代码在执行期会报错. 也就是说, Array.prototype.push.call( 1, ‘first’ )和Array.prototoype.push.call( function(){}, ‘first’ )都达不到预期的效果.

利用访问者,我们来做个有趣的事情. 给一个object对象增加push方法.

var Visitor = {}
Visitor .push = function(){
return Array.prototype.push.apply( this, arguments );
}
var obj = {};
obj.push = Visitor .push;
obj.push( '"first" );
alert ( obj[0] ) //"first"
alert ( obj.length ); //1

JS常用的设计模式(8)——访问者模式的更多相关文章

  1. JS常用的设计模式(17)—— 状态模式

    状态模式主要可以用于这种场景 1 一个对象的行为取决于它的状态 2 一个操作中含有庞大的条件分支语句 回想下街头霸王的游戏. 隆有走动,攻击,防御,跌倒,跳跃等等多种状态,而这些状态之间既有联系又互相 ...

  2. JS常用的设计模式(14)—— 备忘录模式

    备忘录模式在js中经常用于数据缓存. 比如一个分页控件, 从服务器获得某一页的数据后可以存入缓存.以后再翻回这一页的时候,可以直接使用缓存里的数据而无需再次请求服务器. 实现比较简单,伪代码: var ...

  3. JS常用的设计模式(12)—— 迭代器模式

    迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该方法中的内部表示. js中我们经常会封装一个each函数用来实现迭代器. array的迭代器: forEach = functio ...

  4. JS常用的设计模式(13)——组合模式

    组合模式又叫部分-整体模式,它将所有对象组合成树形结构.使得用户只需要操作最上层的接口,就可以对所有成员做相同的操作. 一个再好不过的例子就是jquery对象,大家都知道1个jquery对象其实是一组 ...

  5. JS常用的设计模式(9)——策略模式

    策略模式的意义是定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.一个小例子就能让我们一目了然. 回忆下jquery里的animate方法. $( div ).animate( {&quo ...

  6. JS常用的设计模式(7)—— 外观模式

    外观模式(门面模式),是一种相对简单而又无处不在的模式.外观模式提供一个高层接口,这个接口使得客户端或子系统更加方便调用.用一段再简单不过的代码来表示 var getName = function() ...

  7. JS常用的设计模式(6)——桥接模式

    桥接模式的作用在于将实现部分和抽象部分分离开来, 以便两者可以独立的变化.在实现api的时候, 桥接模式特别有用.比如最开始的singleton的例子. var singleton = functio ...

  8. JS常用的设计模式(5)——代理模式

    代理模式的定义是把对一个对象的访问, 交给另一个代理对象来操作. 举一个例子, 我在追一个MM想给她送一束花,但是我因为我性格比较腼腆,所以我托付了MM的一个好朋友来送. 这个例子不是非常好, 至少我 ...

  9. C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginator插件和knockout.js完成分页功能 图片在线裁剪和图片上传总结 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi

    C#设计模式总结 一. 设计原则 使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性.并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则.开放封闭原则.里氏代替 ...

随机推荐

  1. NeHe OpenGL教程 第三课:颜色渲染

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. c#(特殊集合)

    Stack集合(先进后出) Stack ss = new Stack();//初始化 ss.Push(1);//.Push()一个一个推进,赋值ss.Push(2);ss.Push(3);ss.Pus ...

  3. Sublime 编译出来的是 dos格式,不是unix格式

    在windows下编辑一般都这样的 :set ff=unix就好

  4. 张恭庆编《泛函分析讲义》第二章第2节 $Riesz$ 定理及其应用习题解答

    在本节中, $\scrH$ 均指 $Hilbert$ 空间. 1.在极大闭子空间的交的最佳逼近元 设 $f_1,f_2,\cdots,f_n$ 是 $\scrH$ 上的一组线性有界泛函, $$\bex ...

  5. LPC1768之GPIO

    在AHB总线上以便IO的高速操作,上电默认为输入高电平,支持GPDMA操作. 1只有GPIO0和GPIO2上的引脚能产生中断,/EXIT0~3特殊的外中断引脚. 2P0.29/0.30很特殊要么同为输 ...

  6. 如何利用jQuery进行简单表单验证

    <!DOCTYPE html><html><head><meta charset="utf-8"><meta name=&qu ...

  7. Opencv 2.4.9在Ubuntu下的配置与安装

    [原]Opencv 2.4.9在Ubuntu下的配置安装  Opencv 2.4.9在Ubuntu下的配置与安装 surgewong@gmail.com http://blog.csdn.net/su ...

  8. ruby中将数组转换成hash

    class Arraydef to_h(default=nil)Hash[ *inject([]) { |a, value| a.push value, default || yield(value) ...

  9. .NET技术+25台服务器怎样支撑世界第54大网站(转)

    [编者按]StackOverflow是一个IT技术问答网站,用户可以在网站上 提交和回答问题.当下的StackOverflow已拥有400万个用户,4000万个回答,月PV5.6亿,世界排行第54.然 ...

  10. substring与substr

    一.substring package Test; public class SubstringTest { public static void main(String[] args) { Stri ...