使用AmplifyJS组件配合JavaScript进行编程的指南这篇文章主要介绍了使用AmplifyJS组件配合JavaScript进行编程的指南,AmplifyJS中提供的订阅功能十分强大,需要的朋友可以参考下事件分发的作用在为页面添加各类交互功能时,我们熟知的最简单的做法就是为页面元素绑定事件,然后在事件处理函数中,做我们想要做的动作。就像这样的代码:?123element.onclick=function(event){//Doanything.};如果我们要做的动作不复杂,那么实际逻辑功能的代码,放在这里是可以的。如果今后需要修改,再到这段事件处理函数的位置来修改。再进一步,为了做适当的代码复用,我们可能会把逻辑功能中的一部分分拆到一个函数内:?1234element.onclick=function(event){//Othercodehere.doSomethingElse();};这里的函数doSomethingElse对应的功能可能会在其他地方用到,所以会这样做分拆。此外,可能会有设定坐标这样的功能(假定函数名为setPosition),则还需要用到浏览器事件对象event提供的诸如指针位置一类的信息:?12345element.onclick=function(event){//Othercodehere.doSomethingElse();setPosition(event.clientX,event.clientY);};此处有一个不推荐的做法是直接把event对象传递给setPosition。这是因为,分清逻辑功能和事件侦听两种职责,是一种良好的实践。只让事件处理函数本身接触到浏览器事件对象event,有利于降低代码耦合,方便独立测试及维护。那么,功能越来越多,越来越复杂了会怎么样呢?如果沿用之前的做法,可能是这个样子:?1234567element.onclick=function(event){doMission1();doMission2(event.clientX,event.clientY);doMission3();//...doMissionXX();};虽然这样用也没问题,但这种时候其实就可以考虑更优雅的写法:?123456element.onclick=function(event){amplify.publish(aya:clicked,{x:event.clientX,y:event.clientY});};这种形式就是事件分发,请注意,这里的事件并不是指浏览器原生的事件(event对象),而是逻辑层面的自定义事件。上面的aya:clicked就是一个随便写(really?)的自定义事件名称。显然到这还没结束,为了完成之前的复杂的功能,我们还需要将自定义事件和要做的事关联在一起:?1234amplify.subscribe(aya:clicked,doMission1);//...amplify.subscribe(aya:clicked,doMission2);//...看起来又绕了回来?没错,但这是有用的。一方面,浏览器原生事件的侦听被分离并固化了下来,以后如果逻辑功能有变化,例如减少几个功能,则只需要到自定义事件的关联代码部分做删减,而不需要再关心原生事件。另一方面,逻辑功能的调整变得更为灵活,可以在任意的代码位置通过subscribe添加功能,而且可以自行做分类管理(自定义的事件名)。简单来说,事件分发通过增加一层自定义事件的冗余(在只有简单的逻辑功能时,你就会觉得它是冗余),降低了代码模块之间的耦合度,使得逻辑功能更为清晰有条理,便于后续维护。等下,前面那个出境了好几次的很有存在感的amplify是干什么的?Nice,终于是时候介绍这个了。AmplifyJS事件分发是需要一定的方法来实现的。实现事件分发的设计模式之一,就是发布/订阅(Publish/Subscribe)。AmplifyJS是一个简单的JavaScript库,主要提供了Ajax请求、数据存储、发布/订阅三项功能(每一项都可独立使用)。其中,发布/订阅是核心功能,对应命名是amplify.core。amplify.core是发布/订阅设计模式的一个简洁的、清晰的实现,加上注释一共100多行。读完amplify的源码,就可以比较好地理解如何去实现一个发布/订阅的设计模式。代码全貌amplify.core的源码整体结构如下:(function(global,undefined){varslice=[].slice,subscriptions={};varamplify=global.amplify={publish:function(topic){//...},subscribe:function(topic,context,callback,priority){//...},unsubscribe:function(topic,context,callback){//...}};}(this));可以看到,amplify定义了一个名为amplify的全局变量(作为global的属性),它有3个方法publish、subscribe、unsubscribe。此外,subscriptions作为一个局部变量,它将保存发布/订阅模式涉及的所有自定义事件名及其关联函数。publishpublish即发布,它要求指定一个topic,也就是自定义事件名(或者就叫做话题),调用后,所有关联到某个topic的函数,都将被依次调用:publish:function(topic){//[1]if(typeoftopic!==string){thrownewError(Youmustprovide);}//[2]varargs=slice.call(arguments,1),topicSubscriptions,subscription,length,i=0,ret;if(!subscriptions[topic]){returntrue;}//[3]topicSubscriptions=subscriptions[topic].slice();for(length=topicSubscriptions.length;ilength;i++){subscription=topicSubscriptions[i];ret=subscripti(subscription.context,args);if(ret===false){break;}}returnret!==false;},[1],参数topic必须要求是字符串,否则抛出一个错误。[2],args将取得除topic之外的其他所有传递给publish函数的参数,并以数组形式保存。如果对应topic在subscriptions中没有找到,则直接返回。[3],topicSubscriptions作为一个数组,取得某一个topic下的所有关联元素,其中每一个元素都包括callback及context两部分。然后,遍历元素,调用每一个关联元素的callback,同时带入元素的context和前面的额外参数args。如果任意一个关联元素的回调函数返回false,则停止运行其他的并返回false。subscribe订阅,如这个词自己的含义那样(就像订本杂志什么的),是建立topic和callback的关联的步骤。比较特别的是,amplify在这里还加入了priority(优先级)的概念,优先级的值越小,优先级越高,默认是10。优先级高的callback,将会在publish的时候,被先调用。这个顺序的原理可以从前面的publish的源码中看到,其实就是预先按照优先级从高到低依次排列好了某一topic的所有关联元素。subscribe:function(topic,context,callback,priority){if(typeoftopic!==string){thrownewError(Youmustprovideavalid);}//[1]if(arguments.length===3&&typeofcallback===number){priority=callback;callback=context;context=null;}if(arguments.length===2){callback=context;context=null;}priority=priority||10;//[2]vartopicIndex=0,topics=topic.split(/\s/),topicLength=topics.length,added;for(;topicIndextopicLength;topicIndex++){topic=topics[topicIndex];added=false;if(!subscriptions[topic]){subscriptions[topic]=[];}//[3]vari=subscriptions[topic].length-1,subscriptionInfo={callback:callback,context:context,priority:priority};//[4]for(;i=0;i--){if(subscriptions[topic][i].priority=priority){subscriptions[topic].splice(i+1,0,subscriptionInfo);added=true;break;}}//[5]if(!added){subscriptions[topic].unshift(subscriptionInfo);}}returncallback;},[1],要理解这一部分,请看amplify提供的API示意:?12345amplify.subscribe(stringtopic,functioncallback)amplify.subscribe(stringtopic,objectcontext,functioncallback)amplify.subscribe(stringtopic,functioncallback,numberpriority)amplify.subscribe(stringtopic,objectcontext,functioncallback,numberpriority)可以看到,amplify允许多种参数形式,而当参数数目和类型不同的时候,位于特定位置的参数可能会被当做不同的内容。这也在其他很多JavaScript库中可以见到。像这样,通过参数数目和类型的判断,就可以做到这种多参数形式的设计。[2],订阅的时候,topic是允许空格的,空白符将被当做分隔符,认为是将一个callback关联到多个topic上,所以会使用一个循环。added用作标识符,表明新加入的这个元素是否已经添加到数组内,初始为false。[3],每一个callback的保存,实际是一个对象,除callback外还带上了context(默认为null)和priority。[4],这个循环是在根据priority的值,找到关联元素应处的位置。任何topic的关联元素都是从无到有,且依照priority数值从小到大排列(已排序的)。因此,在比较的时候,是先假设新加入的元素的priority数值较大(优先级低),从数组尾端向前比较,只要原数组中有关联元素的priority数值比新加入元素的小,循环就可以中断,且可以确定地用数组的splice方法将新加入的元素添加在此。如果循环一直运行到完毕,则可以确定新加入的元素的priority数值是最小的,此时added将保持为初始值false。[