基于JavaScript的微博备份应用
鉴于微博管理的日益严格,很多内容都变成了不适合公开或被用户删除。为了保存自己的微博,只好使用一些具有中国特色的应用-微博备份。
现在微博备份工具有两个思路:1.使用微博提供的API,但是微博提供的API目前是无法读取所有的微博消息的。2.通过wap版的微博备份。这是目前为止比较好的一个办法。
但是,作为一名前端攻城师,我们需要更加透明,更加安全的方式来备份自己的微博。
所以这里选择使用JavaScript来做一个微博备份的工具,具体请查看微博备份时光机。
其实实现的思路还是很简单的。
-
o
- 使用bookmarklet载入一段JavaScript,模拟浏览微博的过程,载入页面的全部微博。
- 分析页面上的数据,提取需要的信息并保存到数据库。
- 打开一个新的页面重复第1步和第2步操作直到最后一页。注意不要关闭第一页,因为要通过第一页监控新打开的页面的状态。
- 在全部处理完成之后将,提示保存到本地并将服务器数据删除。
o
o
o
基本的思路就是这样的。
优点:
-
o
- 使用JavaScript技术,所有过程透明,系统不需要您提供任何个人信息或密码信息,你只需要打开你要备份的微博页面,点击备份按钮即可以完成备份.
- 系统提供将备份的页面保存到本地的功能,同时提供JSON格式的元数据,方便进行管理和重用.
- 所有源代码开源
o
o
缺点:
-
o
- 备份过程较为复杂,需要本地等待。
- 需要使用较为先进的浏览器。
- 有时候可能链接失败,断点处继续的功能目前还没有。
o
o
抛砖引玉,欢迎大家指正。
转载请注明原文出处《基于JavaScript的微博备份应用》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
使用AngularJS高效便捷的开发网络应用
Google于最近发布了一款增强HTML的AngularJS框架.它可以使用增强的HTML语言快速的构建Web App.
HTML用来定义静态文档非常赞,但是用来定义web应用的动态视图就显得有点力不从心了.AngularJS能够为你的应用扩展HTML词汇,让你的代码更加具有表现力,更高的可读性,以及更高效的开发速度.
其他的框架使用抽象HTML,CSS及JavaScript的方式或者提供一个规则的方法来操作DOM来解决HTML的短板.但是所有的这些都没有解决HTML并非为动态视图设计的这一根源问题.
AngularJS是完全可扩展的并且能够与其他的框架完美结合.所有的功能都可以被修改或者代替以便适合你的独特的开发流程和功能需求.
AngularJS主要有一下特点:
-
o
- 不唐突的数据绑定.AngularJS自动的将数据从UI一道你的model中,并在数据改变后自动更新到UI.不需要继承类,不需要包装也不需要调用getter/setter方法.你的model可以简单如原始的数组也可以复杂如你自定义的JavaScript对象.
- HTML作为模板.你,你的浏览器,你的编辑器以及你的其他工具早已了解如何使用HTML工作.为什么还要使用其他的格式?AngularJS让你扩展属于你的应用的特定元素,属性,类型,而且这完全符合HTML标准.
- HTML的可重用的组件!AngularyJS让你有能力使用你添加了行为和动画的元素和属性扩展HTML语法.想像这样使用<tab>,<calendar>或者<colorpicker>来代替<div><div><div>…吗?想通过添加一个key=’ctrl-s’来给任意元素添加快捷键吗?你错过<blink>标签了么?所有的这些以及更多都不在是梦想.
- 视图和路由.AngularJS让你通过简单的路由切换子视图.同时你会得到免费的深链接.
- 测试和可测性.发布应用意味着测试他们.我们提供了常见的模拟,我们充分利用依赖注入,并且我们鼓励MVC结构分离行为和视图以便更易测试.它还提供了一个端到端的场景测试器通过了解应用状态来消除测试片段.
o
o
o
o
一个简单的例子,让你输入到input中的内容自动反映到页面其他的地方:
<!doctype html> <html ng-app> <head> <script src="http://code.angularjs.org/angular-1.0.1-ffb27013.min.js"></script> </head> <body> <div> <label>Name:</label> <input type="text" ng-model="yourName" placeholder="Enter a name here"> <hr> <h1>Hello {{yourName}}!</h1> </div> </body> </html>
可以看到确实很简洁.至于效率方面我还没进行测试,源码也还没读,如果感兴趣可以到官网看一下,官网也有实例.
转载请注明原文出处《使用AngularJS高效便捷的开发网络应用》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
渐进增强的无刷新多图片上传控件(iFrame+HTML5)
目标:
实现一个无刷新的,多图片上传控件.(目前已在91美图网中使用)
特点:
采用渐进增强的设计思路,针对支持HTML5新特性比较好的现代浏览器,使用HTML5中的新特性,包括File
对象,XMLHttpRequest中的upload对象,File对象等新增的功能实现较为高级的多图片无刷新能够检测上传进度的上传控件.而对于不支持HTML5
特性的较老的浏览器则使用传统的隐藏iFrame
的形式来实现伪装的多图片上传功能.
优缺点分析:
对于支持HTML5的现代浏览器,可以通过原生的input
控件添加multiple属性实现文件的多选上传.这种方法可以使用XMLHttpReqeust
对象直接上传图片对象,使用较为方便,而且可以在本地获取图片上传的进度.但是目前占市场份额较多的IE浏览器包括IE9以及低于IE9版本的IE浏览器均不支持这项新内容.
所以对于不支持HTML5中的multiple
属性的input[type='file']
的浏览器这里采用了隐藏iframe,将待提交的表单的target设置为iframe
的name,以此来实现变通的无刷新图片上传.事实上,这种方法并不是没有刷新,而是把刷新的操作放到iframe中了而已.由于较旧的浏览器事实上并不支持上传控件的多选,所以这里只能通过多次选择,选中多个文件,操作较为复杂.而且本方法要想实现上传进度必须配合服务器端的查询,增加服务压力.
实现效果:
实现:
首先通过能力检查的方式判断浏览器是否支持HTML5中的新特性,如果支持的话选择HTML5的上传方式,不支持则使用传统的方式实现.
isSupportMultipleFile: window.File && window.FileReader && window.FileList && window.Blob,
在使用HTML5的新功能上传时用的主要内容有:1.<input type="file" />
控件的新属性multiple
,添加了此属性的控件支持同时选择多个文件,在选择完成后会自动生成一个File
对象的集合FileList
对象.这里我们就是通过监控input控件的onchange事件,来获取用户选中的文件.
File对象包括的主要属性:
-
o
- fileName-字符串,文件的名称
- fileSize-无符号的长整型,文件的大小的bytes
- name-字符串,文件名
- size-无符号长整型,File对象引用的文件的大小(bytes)
- type-字符串,File对象引用文件的类型(MIME type)
o
o
o
o
在获取到FileList之后根据每个File对象的内容生成界面中的待上传项目:
addUploadItem: function(guid, filename, percent){}
本方法主要是根据唯一编号,文件名称和当前进度生成一个待上传的条目,用于界面显示,主要操作为DOM操作,这里不再赘述.
接下来就是最主要的文件上传部分的内容了,主要的工作就是:当用户点击上传按钮时,将缓存起来的FileList遍历上传.
这里我采用了每次同时上传5个文件的策略,同时上传的文件太多会造成浏览器崩溃,以及服务器端阻塞超时的情况.具体同时上传文件数目可以根据实际情况决定.
这里要说明一下,使用XMLHttpReqeust对象上传File对象有两种方法:
-
o
- 直接使用XMLHttpRequest将File对象Post到服务器端,但是由于这样发送的内容没有有效的请求信息,只是将文件内容Post到服务器端,所以在服务器端无法通过正常的post获取文件内容,所以要自己读取服务器到的源内容来获取图片.
PHP
中可以通过$GLOBALS["HTTP_RAW_POST_DATA"]
来获取,ASP.NET中可以通过Request.BinaryRead(Request.TotalBytes)
来获取,其他语言您可以自行查找.当然文件的其他属性,比如大小,名称等等可以通过设置请求头的方式来实现,XMLHttpRequest.setReqeustHeader();
- 另外Mozilla Developer NetWork上提供了另外一种方法,即通过FileReader对象来获取File的二进制字符串,然后自己按照标准的结构构造一个请求,通过
XMLHttpReqeust.sendAsBinary
来以二进制流的形式上传,来实现后端服务器像普通模式一样获取内容.但是很遗憾,XMLHttpReqeust.sendAsBinary
这个方法是FireFox私有的方法,标准中没有,其他浏览器也并没有支持,所以经过测试能用后我果断放弃了这个方法.
o
需要了解的技术内容已经清晰了,下面就来看一下实现吧,我用伪代码+注释的形式来说明:
//获取缓存好的FileList对象 var allFiles = fileuploader.cache.files; //新建一个迭代游标用于控制当前同时上传的文件数目 var iterator = 0; //上传的主要方法 var upload = function(){ o//检查如果FileList对象的数目为0则直接退出 oif(allFiles.length == 0){ oreturn; o} o//如果FileList不为空,且当前的迭代游标小于5则执行上传 owhile(allFiles.length > 0 && iterator < 5){ o//弹出一个File ovar file = allFiles.shift(); o//游标加1 oiterator++; o//TODO:获取界面上的条目,并设置状态为开始上传 ovar perDom = fileuploader.uploadItems[file.guid]; o/*样式设置代码省略*/ o//新建XMLHttpRequest上传File文件,通过一个自执行的匿名函数将perDom传给异步请求,留作请求完成使用 o(function(perDom){ ovar req = new Request({ ourl: '/member/img-item/ajax-upload', odataType: 'json', omethod: 'post', osuccess: function(data){ o//检查数据的状态,并处理返回的数据 oif(data.status){ ofileuploader.addImages([data]); o//TODO:设置状态为成功 o/**样式设置代码省略**/ o}else{ o//TODO:设置状态为失败 o/**样式设置代码省略**/ o} o//游标减1表示上传完成 oiterator--; o//检查是否全部上传完毕 oif(allFiles.length == 0){ ofileuploader.close(); o} o//如果游标变为0则重新调用upload方法上传剩余的内容 oif(iterator == 0){ oupload(); o} o}, oprogress: function(e){ o//上传过程中计算上传的进度 ovar percent = parseInt(100 * e.loaded / e.total); operDom.progressBar.style.width = percent + '%'; o} o}); o//设置请求头信息,包括文件的名称大小类型唯一标识等等 oreq.setRequestHeader("DKUPLOADER_NAME", file.name); oreq.setRequestHeader("DKUPLOADER_SIZE", file.size); oreq.setRequestHeader("DKUPLOADER_TYPE", file.type); oreq.setRequestHeader("DKUPLOADER_GUID", file.guid); o//上传文件 oreq.send(file); o})(perDom) o} };
使用HTML5上传图片的内容到这里就结束了.怎么样很简单是不是.下面就继续说一下传统的方法上传的实现.
普通的基于iframe的无刷新文件上传系统已经是老生常谈的问题了,即将Form的target指向iframe,在执行submit的时候,页面不会刷新,只是iframe刷新.只要页面返回的内容中执行javascript调用父窗口的相关对象来更新状态即可.
当然本身这种方法是无法实现获取文件上传进度的,但是在php环境中,通过Alternative PHP Cache(APC)模块的扩展带来的小惊喜,可以获取文件上传的进度.前提是你需要独立安装这个扩展,而且需要你通过脚本定时查询后台来获取上传进度.这么做一是进度不够精确,另外增加服务器负担,除非是文件体积较大,否则实在是没有太大的必要这么做.至于其他的后台语言我没有深入研究,如果有哪位愿意分享还请多多指教.
这里实现选择多个文件是通过将<input type="file"/>
注册onchange
事件,待其选择文件后将其移动到另外一个隐藏容器中并将其缓存起来.而在原来上传按钮的位置重新创建一个新的input对象.这在点击选择文件按钮时会一直有上传选择窗口弹出,来实现伪装的多文件上传.
是实际上传的时候,<input type="file"/>
移动的策略跟第一种情况基本类似,只不过上传文件的方法由XMLHttpRequest改为Form的Submit方法.下面继续已代码说明:
var upload = function(){ o//获取所有缓存的input[type='file']对象 ovar allFiles = fileuploader.cache.files; o//如果数目为零则跳出 oif(allFiles.length <= 0){ fileuploader.close(); return; } //迭代游标 var iterator = 0; var uploadForm = fileuploader.dom.uploadForm; var uploadItems = fileuploader.uploadItems; //先清空form对象内部的内容 uploadForm.innerHTML = ''; //限定一次上传最多5个文件 while(allFiles.length > 0 && iterator < 5){ ovar file = allFiles.shift(); oiterator++; ovar perDom = uploadItems[file.guid]; o//修改状态为正在上传 operDom.status.className = 'message'; operDom.status.innerHTML = "正在上传"; operDom.thumb.innerHTML = '<img src="http://www.91meitu.net/resource/images/ajax_loader_s.gif" />'; o//将input[type='file']对象移动到待上传的表单内部 ouploadForm.appendChild(file); o} o//开始上传 ouploadForm.submit(); }
当然利用iframe跟使用HTML5上传还是有些区别的,因为使用Form提交的内容,通过javascript是无法直接获取上传的状态的,所以要被动等待iframe的内容返回后,通过调用方法来实现完成上传后的下一步操作.
我这里的图片上传控件是一个静态的对象,名字叫fileuploader,所以在iframe中调用的时候可以通过调用fileupload.complete方法来实现图片上传完成后的下一步操作.页面返回的内容是:
<script>window.parent.fileuploader.complete(" . json_encode($result) . ")</script>
而complete方法的具体内容如下:
var complete = function(){ ovar filesInfo = []; ofor(var i = 0, len = data.length; i < len; i++){ ovar imgData = data[i]; ovar perDom = fileuploader.uploadItems[imgData.guid]; oif(imgData.status){ ofilesInfo.push(imgData); o//TODO:设置状态为成功 o/**样式设置代码省略**/ o}else{ o//TODO:设置状态为失败 o/**样式设置代码省略**/ o} o} ofileuploader.addImages(filesInfo); o//继续上传剩余的文件 othis.iframeupload(); }
写到这基本上整个上传控件就写完了,当然还有一些open(),close()方法,一些自定义事件什么的都可以继续丰富和完善.抛砖引玉,欢迎不吝指教!
使用方法,在页面载入完毕后执行fileuploader.open();
文件中使用了我自己写的一个小工具包,打包成zip文件,欢迎大家下载源代码.
点击下载:fileuploader源代码
参考资料:
-
o
- Using files from web applications
- Using the DOM File API in chrome code
- File-Mozilla Developer Network
- File API-W3C
- XMLHttpRequest Level 2
o
o
o
o
转载请注明原文出处《渐进增强的无刷新多图片上传控件(iFrame+HTML5)》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
瀑布布局的JavaScript实现方式
瀑布式布局是一种多列等宽不等高的一种页面展示方式,用于图片来源比较复杂,图片尺寸比较复杂时可以使用的一种展示方式,这种展示方式比较优美,让人有种错落有致的感觉.这种展示方式在淘宝的我要买,新浪微博的广场以及蘑菇街等等网站都有应用.这里是我刚刚做的一个小站91美图
实现布局有三个思路:
-
o
- 最传统的思路,多弄几个容器,分几列,然后往每个列里面插入元素.其实用table分几列实现更加方便:P;
- 使用html5中css3的多列布局来实现.参见w3c标准中的css3多列布局模块;
- 使用绝对布局,通过javascript生成元素的布局位置.
o
o
前两种方法在网上都有比较详细的介绍,我这里就不再多说了,这里主要说一下我做的第三种实现的优缺点以及我的实现思路.
第三种方案是所有的要布局的元素都是绝对定位的,然后通过javascript来判断每个元素位置,动态设置位置实现布局.
缺点
需要使用javascript来遍历元素,然后要根据前一个元素来判断后一个元素的位置,这样可能对一些老版本的浏览器造成负担,特别是IE6这种老古董,而且在javascript失效的时候,整个页面的布局都会乱掉.另外如果整个页面宽度是变化的,则可能每次窗口尺寸改变时都要重新计算所有元素的位置,在页面中元素较多的时候可能会有闪烁的现象.另外如果页面中出现图片,则需要实现定义好图片的尺寸,否则会出现无法正确计算元素位置的情况.
优点
布局更加灵活,元素绝对定位,可以使用javascript灵活操作.页面宽度改变时可以重新布局整个页面.可以使页面的中的元素真正流动起来,让新添加的元素插入到高度最低的列,使页面的低端更加整齐,对插入的元素高地要求较低.可以较为方便的实现延迟加载.
具体实现
最开始我实现的时候是通过使用javascript生成虚拟的列,根据元素的顺序为每个元素分配一个列和行,然后计算每个元素的位置,举个例子,现在假设有四个列:
使其在页面中布局.事实上这个解决办法跟第一种和第二种是一个道理的.最后页面每列的高度差别可能会很大.
//getElements()方法用于获取页面中的元素 var items = getElements(); var columnCount = 4; var columnWidth = 230; var padding = 8; //遍历所有元素 for(var i = 0, len = items.length; i < len; i++){ o//获取当前的元素 ovar currentItem = items[i]; o//获取当前对象的列 ovar currentColumn = (i + 1) % 4; o//获取当前对象的行 ovar currentLevel = parseInt(i / 4); o//有了当前的行跟列可以根据上一层的对象计算出当前对象的高度 ovar left = currentColumn * columnWidth; ovar top = items[i - 4] ? 0 : items[i - 4].style.top + items[i - 4].clientHeight + padding; o//设置当前的位置 ocurrentItem.style.top = top + 'px'; ocurrentItem.style.left = left + 'px'; }
代码和逻辑都比较简单,根据当前的行跟列计算出位置就行了.但是这个逻辑还是会出现元素高地差距过大的情况.看一下新浪weibo的广场图片效果:
可以看到越到最后可能列高度之间的差距会越大.这不是我们想要实现的效果.
所以我这里换了一个思路,虚拟的列还是要有的,但是行的概念我们抛弃掉,我采用的是一个类似流动的概念,新插入的元素是根据每个列的高度,那个高度最低就流向哪个列,最后确保每个列的高度都趋近一致,实现我们想要的效果.当然我们可以采取获取所有元素的高度,然后统一计算一下,获取一个最佳的排列方法,但是这会给浏览器带来较大的计算量,而且如果不断的加载更多的图片我们会得不偿失,所以我们采用的是一个流动的模型,只让当前对象寻找最低的高度然后插入.
这里我实现了一个Column
对象,一个ImgItem
对象.Column
对象用于维护每一列的信息,包括列的最到高度列宽度等列信息.ImgItem
对象保存了一个html节点对象,还有一些设置元素位置,获取当前元素位置的方法.
下面是Column对象的代码:
var Column = function(order){ othis.order = order; othis.maxHeight = 0; othis.columnWidth = 230; othis.left = this.columnWidth * order; othis.lastItem = null; othis.positioned = false; othis.setReferItem = function(item){ othis.lastItem = item; o} othis.getHeight = function(){ oif(this.lastItem){ othis.maxHeight = this.lastItem.getBottom(); o} oreturn this.maxHeight; o} othis.getLeft = function(){ oreturn this.left; o} };
ImgItem对象的代码:
var ImgItem = function(referNode, column){ othis.referNode = referNode; othis.bottom = -1; othis.positioned = false; }; ImgItem.prototype = { o/* o *set the refer node's top o * @param value: Number o */ osetTop: function(value){ othis.referNode.style.top = value + 'px'; o}, o/* o *set the refer node's left o * @param value: Number o */ osetLeft: function(value){ othis.referNode.style.left = value + 'px'; o}, o/* o *get the refer node bottom position o */ ogetBottom: function(){ oif(this.positioned){ oif(this.bottom < 0){ othis.bottom = parseInt(this.referNode.style.top) + this.referNode.clientHeight; o} oreturn this.bottom; o}else{ othrow("current node has not been positioned!"); o} o}, osetPosition: function(column){ othis.positioned = true; othis.setLeft(column.getLeft()); othis.setTop(column.getHeight() + 10); ocolumn.setReferItem(this); o} };
基础打好了,下面要做的就是给元素进行布局了:
//首先根据配置信息中的列数初始化列 for(var i = 0, len = this.config.columnCount; i < len; i++){ othis.columns.push(new Column(i)); } //获取页面上已存在的对象 var liItems = document.getElementById('img_list').getElementsByTagName('li'); //将所有的对象进行布局 for(i = 0, len = liItems.length; i < len; i++){ othis.addNewItem(liItems[i]); }
好吧这里还用到了一个addNewItem方法:
getMinHeightColumn: function(){ ovar minHeight = -1, tempColumn = null; o//遍历所有的列,获取当前高度最低的列,并返回 ofor(var i = 0,len = this.columns.length; i < len; i++){ if(minHeight > this.columns[i].getHeight() || minHeight == -1){ ominHeight = this.columns[i].getHeight(); otempColumn = this.columns[i]; o} o} oreturn tempColumn; }, getMaxHeight: function(){ ovar maxHeight = -1; o//遍历列对象,获取高度最高的列并返回高度 ofor(var i = 0, len = this.columns.length; i < len; i++){ oif(maxHeight < this.columns[i].getHeight()){ omaxHeight = this.columns[i].getHeight(); o} o} oreturn maxHeight; }, addNewItem: function(liItem){ o//设置元素的位置 ovar imgItem = new ImgItem(liItem); oimgItem.setPosition(this.getMinHeightColumn()); othis.cachedItems.push(); o//设置容器的高度 odocument.getElementById('img_list').style.height = this.getMaxHeight() + 'px'; }
基本的布局逻辑已经都齐了,还有的就是lazyload的一些逻辑了,这些逻辑都比较通用.加载后布局对象的逻辑是相同的.这里就不再赘述了.
抛砖引玉,如有谬误还请不吝指教.
转载请注明原文出处《瀑布布局的JavaScript实现方式》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
JavaScript与设计模式
这篇文章主要是记录自己的一些想法,同时也能够让刚刚接触编程的人对如何构建健壮易用的代码有所了解,而有丰富编程经验的高手能够给予意见和建议.这里打算把这个议题写成一个系列文章,这篇就算做一个引子,废话可能多一点,见谅:P
模式实际上是生活中很常见的概念,它并不神奇.举个例子,我们迈过一个台阶,这里很自然的你就使用了一种模式,要先抬高一条腿,然后将脚落到前面的台阶上,然后再抬高另外一条腿,放到另外一个台阶上,这个过程很自然的完成了.当然你也可以选择两条腿同时蹦上去,或者加上手,爬上去,但这不是首选的方式.我们迈过台阶的时候实际就是一个迈台阶的模式,你不用考虑很久直接就会迈过去,事实上如果你连迈台阶都要考虑很久的话…可能你的日子就没法正常过了.
程序设计中的模式也是对程序设计中可能碰到的问题的解决方法进行抽象和总结得出的一些范式.你可以在解决典型问题的时候使用相关的模式,相当于站在巨人的肩上,只有基础打好了,后面才会有更好的发展.合理的使用设计模式能够使你构建出健壮,易扩展,易维护的程序.
设计模式方面的资料非常多,但是绝大多数都是针对强类型的语言实现写的,如Java等,而JavaScript则是弱类型的语言,它没有类,接口,抽象类,继承,重载,多态等概念.所以在实现的时候有更多的变通余地,更加灵活,但是随之而来的就是不利于控制.所以编写优秀的JavaScript代码对编程人员的要求反而更高,因为很多东西编译器无法替你检查,你需要自己去控制.
先说类,JavaScript中没有类的概念,但是Function对象可以实现类的大部分功能,它可以作为一个完整的对象来使用,其实我理解的类就是一个有自己属性的,有自己行为的一个抽象,我们没必要拘泥于类的这个概念.这方面的资料很多,这里就不多说了.
再说接口,这个概念JavaScript确实没有,也没类似的代替内容,接口在我理解中其实是签订的一个契约,继承接口的类必须尊重契约做事,至于这个契约是实质的契约还是口头的约定,其形式更大于内容.当然有接口,编译器会在编译的时候发现不遵守契约的坏分子,让其无所遁形,给开发者带来很大的便利.如果所有的类都是君子的话,这个检查就没什么必要了是吧,我相信每个写JavaScript的程序员都是君子:)这里的接口可以是我们的一段注释,我们约定这里必须存在哪些属性,哪些方法,实现的时候就要做到这些,当然我们也可以设计一个额外的功能用于检测目标对象是否真正的实现了相关的方法.
一下是一个简单的实例,我们来定义一个电器类的接口:
code-1:
/* Interface IElectrialEquipment { Number Voltage; //额定电压 Number Current; //额定电流 function void TurnOn(); //打开 function void TurnOff(); //关闭 } */
我们把这段注释拿来当我们的契约,这里我们不提接口.我们要实现一个实现这个契约的电视机,或者电冰箱.
那我的实现代码应该是这个样子的:
var TV = function(voltage, current){ //Implement IElectrialEquipment othis.Voltage = voltage; othis.Current = current; othis.TurnOn = function(){ o//打开电视机 oconsole.log('电视启动了'); o}; othis.TurnOff = function(){ o//关闭电视机 oconsole.log('电视关闭了'); o}; othis.TurnDown = function(){ o//调低音量 o} othis.TurnUp = function(){ o//调高音量 o} } var Fridge = function(voltage, current){ othis.Voltage = voltage; othis.Current = current; othis.TurnOn = function(){ o//打开电冰箱 oconsole.log('冰箱启动了'); o}; othis.TurnOff = function(){ o//关闭电冰箱 oconsole.log('冰箱关闭了'); o}; othis.OpenDoor = function(){ o//打开冰箱门 o}; othis.CloseDoor = function(){ o//关闭冰箱门 o} } //现在我们再来做一个万能遥控器 var PowerfulRemoteController = function(IElectrialEquipment){ othis.TurnOn = function(){ oIElectrialEquipment.TurnOn(); o} othis.TurnOn = function(){ oIElectrialEquipment.TurnOff(); o} othis.ShowInfo = function(){ oconsole.log('额定电压是: ' + IElectrialEquipment.Voltage + ' 额定电流是: ' + IElectrialEquipment.Current); o} } //现在我要打开冰箱,不然东西全坏了 new PowerfulRemoteController(new Fridge(225, 5)).TurnOn(); //现在我要打开电视看电视,看点新闻联播吧 new PowerfulRemoteController(new TV(225, 2)).TurnOn();
这里如果我们要继续控制我们的微波炉或者电脑,我们只需要按照我们定义好的契约实现相关的对象就可以使用我们的万能遥控器控制了,而且我们的万能遥控器完全不需要修改任何内容,这也就是我们常说的对修改关闭,对扩展开放原则.大家可以在Fx中的命令行里面试一下.但是弊端也有,就是如果某位程序员在实现一个电器的时候没有实现TurnOff或者TurnOn方法,编译器是不会报错的.当然我们的万能遥控器也就失效了.所以这种方法对团队里面的开发人员要求比较高,大家都要是君子,做事情都要按契约来.
事实上我们也可以定义一个检测对象,用检测对象强制检测一下实现的对象中是否含有我们需要的属性或方法,如果没有则报错.
其他的重载多态JavaScript中也没有相应的实现,只能通过一些手段来实现类似的功能,比如重载,JavaScript中后定义的方法会完全覆盖先前定义的方法,这个可以拿来凑合一下当作重载。JavaScript对传入实参的类型和数目没有强制的要求,所以可以通过判断传入的实参的数目和类型来做不同的操作实现类似多态的功能。
转载请注明原文出处《JavaScript与设计模式》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
表单验证工具V0.2
是的,我又把那个表单验证工具重构了一遍。事实证明上一个版本的表单验证工具API设计的还算是比较成功,起码这个版本完全重构后,基本的API没有改动。只是对实现部分的代码进行了彻底的重构。由于使用了jQuery的ajax相关功能,所以需要本控件需要jQuery支持。
上一个版本中是把所有的规则存储到一个缓存中,然后通过统一的方法进行节点的验证,这样带来的不便就是需要在全局对所有的节点进行控制,现在是将检查的节点封装一个对象,让数据和行为联系更加紧密,每个人只干自己的事情,逻辑上更加清晰。全局validate
对象只负责初始化和最终表单整体的验证,以及提供一些工具方法。另外将提示信息也封装成为一个对象,便于提示信息样式的更改。
validate
对象现在只包含两个对外开放的方法,一个是init
,一个是check
。init
方法用于初始化,传入相关的对象id,规则,事件等信息,check
方法用于检查整个表单是否通过验证,当表单中含有ajax验证时,会在验证通过后调用回调函数。
check
方法有个可选的回调函数参数,当验证规则中存在ajax验证时,控件并不能即时给出验证结果,具体的验证结果会在ajax回调完成后得出。所以这里只要存在尚未验证过的ajax验证,check
方法总是会返回false
。如果通过调用后想执行一些操作,就需要将需要操作的步骤写到check
方法的回调函数中。这样在validate
在进行完ajax检查并且通过验证后会调用方法。
VNode
对象代码的主体结构如下:
var VNode = function(rule){ oself.check = function(isLocal){ o//local check o//ajax check o}; oself.checkAjax = function(fn){}; oself.reset = function(){}; oself.checkManul = function(isLocal){}; oself.checkEvent = function(e){}; oself.resetEvent = function(){}; oif(rule.trigger){ orule.trigger.call(self); o}else{ o$(self.node).bind(validate.config.trigger, self.checkEvent); o} o$(self.node).bind(validate.config.reset, self.resetEvent); };
这里VNode
包含了所有的数据,规则,以及验证的逻辑,验证的时候要做的就是新建一个VNode
对象,剩下的工作交个VNode
来做就好了。VNode
对象主要是根据提供的规则信息获取要验证的元素,然后对要验证的元素按照一定的次序进行验证,包括自定义的验证方法,长度,自定义的正则表达式,系统提供的正则验证,ajax验证。所有的验证都是验证规则存在是才进行的。
VNode
对象也提供了两个钩子,一个是自定义验证函数,一个是自定义触发验证函数。自定义验证函数用于处理复杂逻辑的验证,自定义触发验证函数默认取代blur
方法,用于日历控件等等选择方式的验证。
还有一个重要的对象就是HintMessage
对象,这个对象其实就是根据给出的引用节点,自动计算出提示信息该显示的位置。当然它有相应的参数控制显示的偏移量,默认是与给出的节点位置重合的。当然新实例化一个HintMessage
对象并不能显示出提示信息,要想显示提示信息还需要调用HintMessage.show(message, messageType)
方法来显示相应的信息以及相应的信息类型。信息类型包含以下几种:
//错误 HintMessage.ERROR = 0; //警告 HintMessage.WARNING = 1; //消息 HintMessage.MESSAGE = 2; //成功 HintMessage.SUCCESS = 3;
每种消息类型对应不同的样式名称,你可以通过自定义样式来显示不同样式的提示信息。当然HintMessage还有一点要注意就是给window
对象绑定onresize
事件,当窗口改变大小的时候需要重新计算提示信息显示的位置。绝对定位带来方便的同时也有些副作用。
相关文章:
o
- 表单验证工具
转载请注明原文出处《表单验证工具V0.2》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。