一,開篇分析javascript
Hi,你們好!大熊君又和你們見面了,還記得上一篇文章嗎。主要講述了以「jQuery的方式如何開發插件」,以及過程化設計與面向對象思想設計相結合的方式是css
如何設計一個插件的,兩種方式各有利弊取長補短,本系列文章是以學習爲導向的,具體場景你們本身定奪使用方式。那麼今天這篇文章咱們說點什麼那?嘿嘿嘿html
。咱們接着上篇文章對不足的地方進行重構,以深刻淺出的方式來逐步分析,讓你們有一個按部就班提升的過程。廢話少說,進入正題。讓咱們先來回顧一下以前的java
Js部分的代碼,以下:api
1 function ItemSelector(elem,opts){ 2 this.elem = elem ; 3 this.opts = opts ; 4 } ; 5 var ISProto = ItemSelector.prototype ; 6 ISProto.getElem = function(){ 7 return this.elem ; 8 } ; 9 ISProto.getOpts = function(){ 10 return this.opts ; 11 } ; 12 /* data manip*/ 13 ISProto._setCurrent = function(current){ 14 this.getOpts()["current"] = current ; 15 } ; 16 ISProto.getCurrentValue = function(current){ 17 return this.getOpts()["current"] ; 18 } ; 19 /* data manip*/ 20 ISProto.init = function(){ 21 var that = this ; 22 this.getOpts()["current"] = null ; // 數據遊標 23 this._setItemValue(this.getOpts()["currentText"]) ; 24 var itemsElem = that.getElem().find(".content .items") ; 25 this.getElem().find(".title div").on("click",function(){ 26 itemsElem.toggle() ; 27 }) ; 28 this.getElem().find(".title span").on("click",function(){ 29 itemsElem.toggle() ; 30 }) ; 31 $.each(this.getOpts()["items"],function(i,item){ 32 item["id"] = (new Date().getTime()).toString() ; 33 that._render(item) ; 34 }) ; 35 } ; 36 ISProto._setItemValue = function(value){ 37 this.getElem().find(".title div").text(value) 38 } ; 39 ISProto._render = function(item){ 40 var that = this ; 41 var itemElem = $("<div></div>") 42 .text(item["text"]) 43 .attr("id",item["id"]) ; 44 if("0" == item["disabled"]){ 45 itemElem.on("click",function(){ 46 var onChange = that.getOpts()["change"] ; 47 that.getElem().find(".content .items").hide() ; 48 that._setItemValue(item["text"]) ; 49 that._setCurrent(item) ; 50 onChange && onChange(item) ; 51 }) 52 .mouseover(function(){ 53 $(this).addClass("item-hover") ; 54 }) 55 .mouseout(function(){ 56 $(this).removeClass("item-hover") ; 57 }) ; 58 } 59 else{ 60 itemElem.css("color","#ccc").on("click",function(){ 61 that.getElem().find(".content .items").hide() ; 62 that._setItemValue(item["text"]) ; 63 }) ; 64 } 65 itemElem.appendTo(this.getElem().find(".content .items")) ; 66 } ;
效果以下圖所示:app
a)------非可操做狀態dom
b)------可操做狀態ide
(二),打開思路,進行重構學習
你們從代碼不難看出,已經經過「Js」中的語法特性,以面向對象的方式進行了有效的組織,比鬆散的過程化形式的組織方式好多了,可是仍然會發現有不少不足的地方。測試
(1),裏面重複代碼太多
(2),職責劃分不清晰
(3),流程梳理不健全
咱們基於以上幾點進行有效的重構,咱們首先要梳理一下這個組件的需求,功能點以下:
(1),初始化配置組件
1 $(function(){ 2 var itemSelector = new ItemSelector($("#item-selector"),{ 3 currentText : "Please Choose Item" , 4 items : [ 5 { 6 text : "JavaScript" , 7 value : "js" , 8 disabled : "1" 9 } , 10 { 11 text : "Css" , 12 value : "css" , 13 disabled : "0" 14 } , 15 { 16 text : "Html" , 17 value : "html" , 18 disabled : "0" 19 } 20 ] , 21 }) ; 22 itemSelector.init() ; 23 }) ;
這塊代碼很清晰,不須要作任何修改,可是你們能夠基於以上配置擴展功能,好比增長配置項「mode」支持多種選項方式。如:「checkbox勾選模式」。
接下來是要完成初始化邏輯,以下:
1 ISProto.init = function(){ 2 var that = this ; 3 this.getOpts()["current"] = null ; // 數據遊標 4 this._setItemValue(this.getOpts()["currentText"]) ; 5 var itemsElem = that.getElem().find(".content .items") ; 6 this.getElem().find(".title div").on("click",function(){ 7 itemsElem.toggle() ; 8 }) ; 9 this.getElem().find(".title span").on("click",function(){ 10 itemsElem.toggle() ; 11 }) ; 12 $.each(this.getOpts()["items"],function(i,item){ 13 item["id"] = (new Date().getTime()).toString() ; 14 that._render(item) ; 15 }) ; 16 } ;
這段代碼問題不少,職責不明確,初始化邏輯包含了功能點的細節實現。
再繼續看渲染部分代碼:
1 ISProto._render = function(item){ 2 var that = this ; 3 var itemElem = $("<div></div>") 4 .text(item["text"]) 5 .attr("id",item["id"]) ; 6 if("0" == item["disabled"]){ 7 itemElem.on("click",function(){ 8 var onChange = that.getOpts()["change"] ; 9 that.getElem().find(".content .items").hide() ; 10 that._setItemValue(item["text"]) ; 11 that._setCurrent(item) ; 12 onChange && onChange(item) ; 13 }) 14 .mouseover(function(){ 15 $(this).addClass("item-hover") ; 16 }) 17 .mouseout(function(){ 18 $(this).removeClass("item-hover") ; 19 }) ; 20 } 21 else{ 22 itemElem.css("color","#ccc").on("click",function(){ 23 that.getElem().find(".content .items").hide() ; 24 that._setItemValue(item["text"]) ; 25 }) ; 26 } 27 itemElem.appendTo(this.getElem().find(".content .items")) ; 28 } ;
問題很明顯,發現了重複性的操做,應該進行合理的抽象,已達到複用的目的。
整個組建的流程包括初始化,渲染(事件綁定),還有就是相關的數據操做方法以及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 ! } ;
第三步,創建數據操做接口:
1 ISProto._setCurrent = function(){ 2 // put you code here ! 3 } ; 4 ISProto._getCurrent = function(){ 5 // put you code here ! 6 } ;
還有一些參照下面的完整源碼,這裏只是說的思路。
(三),完整代碼以供學習,本代碼已通過測試
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多選模式,如今只是默認下拉模式。
哈哈哈,本篇結束,未完待續,但願和你們多多交流夠溝通,共同進步。。。。。。呼呼呼……(*^__^*)