这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
本文是 javascript 插件化开发系列教程的第六篇文章,还是重点对上一篇文章不足的地方进行改进重构,逐步分析让大家能有一个新的认识,希望小伙伴们能够喜欢。
一,开篇分析
今天这篇文章我们说点什么那?嘿嘿嘿。我们接着上篇文章对不足的地方进行重构,以深入浅出的方式来逐步分析,让大家有一个循序渐进提高的过程。废话少说,进入正题。让我们先来回顾一下之前的
Js 部分的代码,如下:
- function ItemSelector(elem,opts){
- this.elem = elem ;
- this.opts = opts ;
- } ;
- var ISProto = ItemSelector.prototype ;
- ISProto.getElem = function(){
- return this.elem ;
- } ;
- ISProto.getOpts = function(){
- return this.opts ;
- } ;
- /* data manip*/
- ISProto._setCurrent = function(current){
- this.getOpts()["current"] = current ;
- } ;
- ISProto.getCurrentValue = function(current){
- return this.getOpts()["current"] ;
- } ;
- /* data manip*/
- ISProto.init = function(){
- var that = this ;
- this.getOpts()["current"] = null ; // 数据游标
- this._setItemValue(this.getOpts()["currentText"]) ;
- var itemsElem = that.getElem().find(".content .items") ;
- this.getElem().find(".title div").on("click",function(){
- itemsElem.toggle() ;
- }) ;
- this.getElem().find(".title span").on("click",function(){
- itemsElem.toggle() ;
- }) ;
- $.each(this.getOpts()["items"],function(i,item){
- item["id"] = (new Date().getTime()).toString() ;
- that._render(item) ;
- }) ;
- } ;
- ISProto._setItemValue = function(value){
- this.getElem().find(".title div").text(value)
- } ;
- ISProto._render = function(item){
- var that = this ;
- var itemElem = $("<div></div>")
- .text(item["text"])
- .attr("id",item["id"]) ;
- if("0" == item["disabled"]){
- itemElem.on("click",function(){
- var onChange = that.getOpts()["change"] ;
- that.getElem().find(".content .items").hide() ;
- that._setItemValue(item["text"]) ;
- that._setCurrent(item) ;
- onChange && onChange(item) ;
- })
- .mouseover(function(){
- $(this).addClass("item-hover") ;
- })
- .mouseout(function(){
- $(this).removeClass("item-hover") ;
- }) ;
- }
- else{
- itemElem.css("color","#ccc").on("click",function(){
- that.getElem().find(".content .items").hide() ;
- that._setItemValue(item["text"]) ;
- }) ;
- }
- itemElem.appendTo(this.getElem().find(".content .items")) ;
- } ;
效果如下图所示:
a)------ 非可操作状态
b)------ 可操作状态
(二),打开思路,进行重构
大家从代码不难看出,已经通过 "Js" 中的语法特性,以面向对象的方式进行了有效的组织,比松散的过程化形式的组织方式好多了,但是仍然会发现有很多不足的地方。
(1),里面重复代码太多
(2),职责划分不清晰
(3),流程梳理不健全
我们基于以上几点进行有效的重构,我们首先要梳理一下这个组件的需求,功能点如下:
(1),初始化配置组件
- $(function(){
- var itemSelector = new ItemSelector($("#item-selector"),{
- currentText : "Please Choose Item" ,
- items : [
- {
- text : "JavaScript" ,
- value : "js" ,
- disabled : "1"
- } ,
- {
- text : "Css" ,
- value : "css" ,
- disabled : "0"
- } ,
- {
- text : "Html" ,
- value : "html" ,
- disabled : "0"
- }
- ] ,
- }) ;
- itemSelector.init() ;
- }) ;
这块代码很清晰,不需要做任何修改,但是大家可以基于以上配置扩展功能,比如增加配置项 "mode" 支持多种选项方式。如:"checkbox 勾选模式"。
接下来是要完成初始化逻辑,如下:
- ISProto.init = function(){
- var that = this ;
- this.getOpts()["current"] = null ; // 数据游标
- this._setItemValue(this.getOpts()["currentText"]) ;
- var itemsElem = that.getElem().find(".content .items") ;
- this.getElem().find(".title div").on("click",function(){
- itemsElem.toggle() ;
- }) ;
- this.getElem().find(".title span").on("click",function(){
- itemsElem.toggle() ;
- }) ;
- $.each(this.getOpts()["items"],function(i,item){
- item["id"] = (new Date().getTime()).toString() ;
- that._render(item) ;
- }) ;
- } ;
这段代码问题很多,职责不明确,初始化逻辑包含了功能点的细节实现。
再继续看渲染部分代码:
- ISProto._render = function(item){
- var that = this ;
- var itemElem = $("<div></div>")
- .text(item["text"])
- .attr("id",item["id"]) ;
- if("0" == item["disabled"]){
- itemElem.on("click",function(){
- var onChange = that.getOpts()["change"] ;
- that.getElem().find(".content .items").hide() ;
- that._setItemValue(item["text"]) ;
- that._setCurrent(item) ;
- onChange && onChange(item) ;
- })
- .mouseover(function(){
- $(this).addClass("item-hover") ;
- })
- .mouseout(function(){
- $(this).removeClass("item-hover") ;
- }) ;
- }
- else{
- itemElem.css("color","#ccc").on("click",function(){
- that.getElem().find(".content .items").hide() ;
- that._setItemValue(item["text"]) ;
- }) ;
- }
- itemElem.appendTo(this.getElem().find(".content .items")) ;
- } ;
问题很明显,发现了重复性的操作,应该进行合理的抽象,已达到复用的目的。
整个组建的流程包括初始化,渲染(事件绑定),还有就是相关的数据操作方法以及 dom 操作的辅助方法。
综上所述,经过简单的梳理后,我们应该建立起功能的操作目的以及流程主线的任务分配,各负其责。
所以我们重构的目的很明确了,对!就是进行功能点的抽象,友好的职责划分,那么我们如何实现那?
第一步,建立流程功能方法:(方法接口)
- ISProto.init = function(){
- // put you code here !
- } ;
- ISProto._render = function(){
- // put you code here !
- } ;
第二部,建立抽象后的方法接口:
- ISProto._fnItemSelectorDelegateHandler = function(){
- // put you code here !
- } ;
- ISProto._fnTriggerHandler = function(){
- // put you code here !
- } ;
- ISProto._addOrRemoveClass = function(){
- // put you code here !
- } ;
第三步,建立数据操作接口:
- ISProto._setCurrent = function(){
- // put you code here !
- } ;
- ISProto._getCurrent = function(){
- // put you code here !
- } ;
还有一些参照下面的完整源码,这里只是说的思路。
(三),完整代码以供学习,本代码已经过测试
- function ItemSelector(elem,opts){
- this.elem = elem ;
- this.opts = opts ;
- this.current = -1 ; // 数据游标
- } ;
- var ISProto = ItemSelector.prototype ;
- /* getter api*/
- ISProto.getElem = function(){
- return this.elem ;
- } ;
- ISProto.getOpts = function(){
- return this.opts ;
- } ;
- ISProto._getCurrent = function(){
- return this.current ;
- } ;
- /* getter api*/
- /* data manip*/
- ISProto._setCurrent = function(current){
- this.current = current ;
- } ;
- ISProto._setItemText = function(text){
- this.getElem().find(".title div").text(text) ;
- } ;
- /* data manip*/
- /* update on 2015 1/31 23:38 */
- ISProto._fnTriggerHandler = function(index,text,value){
- if(this._isDisabled(value)){
- index = -1 ;
- text = this.getOpts()["currentText"] ;
- }
- this._setItemText(text) ;
- this._setCurrent(index) ;
- this.getElem().find(".content .items").hide() ;
- } ;
- ISProto._addOrRemoveClass = function(elem,className,addIs){
- if(addIs){
- elem.addClass(className) ;
- }
- else{
- elem.removeClass(className) ;
- }
- } ;
- ISProto._fnItemSelectorDelegateHandler = function(){
- var that = this ;
- this.getElem().on("click","[data-toggle]",function(){
- that.getElem().find(".content .items").toggle() ;
- }) ;
- } ;
- ISProto._isDisabled = function(value){
- return ("1" == value) ? true : false ;
- } ;
- /* update on 2015 1/31 23:38 */
- ISProto.init = function(){
- var that = this ;
- this._fnItemSelectorDelegateHandler() ;
- $.each(this.getOpts()["items"],function(i,item){
- item["index"] = i ;
- that._render(item) ;
- }) ;
- this._fnTriggerHandler(this._getCurrent(),this.getOpts()["currentText"],"1") ;
- } ;
- ISProto._render = function(item){
- var that = this ;
- var itemElem = $("<div></div>").text(item["text"]).attr("id",item["index"]) ;
- var activeClass = ("0" == item["disabled"]) ? "item-hover" : "item-disabled-hover" ;
- itemElem.on("click",function(){
- that._fnTriggerHandler(item["index"],item["text"],item["disabled"]) ;
- })
- .mouseover(function(){
- that._addOrRemoveClass($(this),activeClass,true) ;
- })
- .mouseout(function(){
- that._addOrRemoveClass($(this),activeClass,false) ;
- }) ;
- itemElem.appendTo(this.getElem().find(".content .items")) ;
- } ;
(四),最后总结
(1),面向对象的思考方式合理分析功能需求。
(2),以类的方式来组织我们的插件逻辑。
(3),不断重构上面的实例,如何进行合理的重构那?不要设计过度,要游刃有余,推荐的方式是过程化设计与面向对象思想设计相结合。
(4),下篇文章中会扩展相关功能,比如 "mode" 这个属性,为 "1" 时支持 checkbox 多选模式,现在只是默认下拉模式。
看我本文,是不是要比上一篇代码优秀了很多呢,小伙伴们自己做项目也应该多想多做,尽量使自己的代码更加的合理。
来源: http://www.phperz.com/article/17/0430/272992.html