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. HUST 1010 The Minimum Length(KMP,最短循环节点,即i-Next[i])

    题意: 有一个字符串A,假设A是“abcdefg”,  由A可以重复组成无线长度的AAAAAAA,即“abcdefgabcdefgabcdefg.....”. 从其中截取一段“abcdefgabcde ...

  2. 1小时vpn coding让开发更简单 或https://www.imfreevpn.org/

  3. 反人类的MyEclipse之-eclipse设置花括号换行显示

    http://www.cnblogs.com/zhwl/archive/2012/12/17/2821806.html 习惯了C的代码风格,用Eclipse的风格,实在是看得卵子痛.尤其是大括号放在最 ...

  4. Java SE 第十六讲----面向对象特征之多态

    1.多态:polymorphism:我们说的子类就是父类(玫瑰是花,男子是人),因此多态的意思就是:父类型的引用可以指向子类的对象 public class PolyTest { public sta ...

  5. java中的final总结

    Java关键字final有最终的,不可改变的含义,它可以修饰非抽象类.非抽象类成员方法和变量. 报错:类"TestFinal"要么是abstract,要么是final的,不能两个都 ...

  6. 算法库:Matlab与C++混合编程

    算法库:Matlab与C++混合编程 最近做光流算法预演过程中,下载的源码中涉及到了Matlab和C++的混合编程.在同事Matlab2014的环境下,程序到是一下就运行通过了.但在我这Matlab2 ...

  7. POJ - 1159 Palindrome(dp-回文变形)

    d.求对字符串最少添加几个字符可变为回文串. s. 法1:直接对它和它的逆序串求最长公共子序列长度len.N-len即为所求.(N为串长度) 因为,要求最少添加几个字符,我们可以先从原串中找到一个最长 ...

  8. iOS 5.0 后UIViewController新增:willMoveToParentViewController和didMoveToParentViewCon[转]

    在iOS 5.0以前,我们在一个UIViewController中这样组织相关的UIView   在以前,一个UIViewController的View可能有很多小的子view.这些子view很多时候 ...

  9. JAVA集合学习

    JAVA中有几种常用的集合类.分别是List,Set,Map等 提示:Eclipse中自动导入包的快捷键  Ctrl+Shift+O 一.List类 父接口:该类是Collection集合接口的子接口 ...

  10. Win8系统 Python安装 - 入门

    原文:http://www.blogbus.com/hx1987-logs/271955446.html 安装python (1)下载python安装包,下载网站https://www.python. ...