利用策略模式與裝飾模式擴(kuò)展JavaScript表單驗證功能
簡單的表單驗證
html結(jié)構(gòu)
<!-- validata.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Validata</title> </head> <body> <form id="form"> <label for="username">賬號:</label><input id="username" type="text"><br> <label for="password">密碼:</label><input id="password" type="password"><br> <label for="phonenum">手機(jī):</label><input id="phonenum" type="text"><br> <input id="submit" type="button" value="提交"> </form> <p id="warn"></p> <script src="validata.js"></script> </body> </html>
首先先簡單地實現(xiàn)以下這個功能
之后再用設(shè)計模式豐滿
// validata.js var form = document.getElementById('form'), warn = document.getElementById('warn'); var validata = function(){ if(form.username.value === ''){ return warn.textContent = '賬號不能為空'; } if(form.password.value === ''){ return warn.textContent = '密碼不能為空'; } if(form.phonenum.value === ''){ return warn.textContent = '手機(jī)號不能為空'; } var msg = { username: form.username.value, password: form.password.value, phonenum: form.phonenum.value } //ajax('...', msg); ajax提交數(shù)據(jù)略 return warn.textContent = '用戶信息已成功提交至服務(wù)器'; } form.submit.onclick = function(){ validata(); }
然后分析以下代碼
validata這個函數(shù)毫無復(fù)用性可言,除此之外存在兩個問題
- 函數(shù)同時承擔(dān)了驗證和提交兩個職責(zé),違背單一職責(zé)原則
- 驗證功能擴(kuò)展性差,要想添加驗證規(guī)則就必須深入函數(shù)內(nèi)部,違反開放-封閉原則
所以我們需要對此進(jìn)行改進(jìn)
裝飾模式重構(gòu)
先來用裝飾模式解決一下函數(shù)多職責(zé)問題
方法也很簡單
改進(jìn)一下AOP前置裝飾函數(shù)(Function.prototype.before)
當(dāng)擴(kuò)展函數(shù)(beforeFn)返回false則不執(zhí)行當(dāng)前函數(shù)
然后令表單驗證函數(shù)成為表單提交函數(shù)的前置裝飾
這樣提交前就會進(jìn)行驗證,若驗證失敗,就不會提交數(shù)據(jù)
var form = document.getElementById('form'), warn = document.getElementById('warn'); Function.prototype.before = function(beforeFn){ var self = this; return function(){ if(beforeFn.apply(this, arguments) === false) return; return self.apply(this, arguments); } }//改進(jìn)的AOP前置裝飾函數(shù) var validata = function(){ if(form.username.value === ''){ warn.textContent = '賬號不能為空'; return false; } if(form.password.value === ''){ warn.textContent = '密碼不能為空'; return false; } if(form.phonenum.value === ''){ warn.textContent = '手機(jī)號不能為空'; return false; } } var submitMsg = function(){ //將提交的功能從validata函數(shù)中提取出來 var msg = { username: form.username.value, password: form.password.value, phonenum: form.phonenum.value } //ajax('...', msg); return warn.textContent = '用戶信息已成功提交至服務(wù)器'; } submitMsg = submitMsg.before(validata); //讓表單驗證函數(shù)成為表單提交函數(shù)的裝飾者 form.submit.onclick = function(){ submitMsg(); };
策略模式重構(gòu)
下面就該解決函數(shù)缺乏彈性的問題
使用策略模式就需要有策略對象/類和環(huán)境對象/類
毫無疑問策略對象中就應(yīng)該裝著校驗規(guī)則
又考慮到頁面可能不止有一個驗證表單
最好寫成工廠-類的形式
完整代碼如下
var form = document.getElementById('form'), warn = document.getElementById('warn'); Function.prototype.before = function(beforeFn){ var self = this; return function(){ if(beforeFn.apply(this, arguments) === false) return; return self.apply(this, arguments); } } var vldStrategy = { //策略對象-驗證規(guī)則 isNonEmpty: function(value, warnMsg){ //輸入不為空 if(value === '') return warnMsg; }, isLongEnough: function(value, length, warnMsg){ //輸入足夠長 if(value.length < length) return warnMsg; }, isShortEnough: function(value, length, warnMsg){ //輸入足夠短 if(value.length > length) return warnMsg; }, isMobile: function(value, warnMsg){ //手機(jī)號驗證 var reg = /^1[3|5|8][0-9]{9}$/; if(!reg.test(value)) return warnMsg; } } var Validator = function(){ //環(huán)境類 this.rules = []; //數(shù)組用于存放負(fù)責(zé)驗證的函數(shù) }; Validator.prototype.add = function(domNode, ruleArr){ //添加驗證規(guī)則 var self = this; for(var i = 0, rule; rule = ruleArr[i++];){ (function(rule){ var strategyArr = rule.strategy.split(':'), warnMsg = rule.warnMsg; self.rules.push(function(){ var tempArr = strategyArr.concat(); var ruleName = tempArr.shift(); tempArr.unshift(domNode.value); tempArr.push(warnMsg); return vldStrategy[ruleName].apply(domNode, tempArr); }); })(rule); } return this; }; Validator.prototype.start = function(){ //開始驗證表單 for(var i = 0, vldFn; vldFn = this.rules[i++];){ var warnMsg = vldFn(); if(warnMsg){ warn.textContent = warnMsg; return false; } } } var vld = new Validator(); vld.add(form.username, [ { strategy: 'isNonEmpty', warnMsg: '賬號不能為空' }, { strategy: 'isLongEnough:4', warnMsg: '賬號不能小于4位' }, { strategy: 'isShortEnough:20', warnMsg: '賬號不能大于20位' } ]).add(form.password, [ { strategy: 'isNonEmpty', warnMsg: '密碼不能為空' } ]).add(form.phonenum, [ { strategy: 'isNonEmpty', warnMsg: '手機(jī)號不能為空' }, { strategy: 'isMobile', warnMsg: '手機(jī)號格式不正確' } ]); var submitMsg = function(){ var msg = { username: form.username.value, password: form.password.value, phonenum: form.phonenum.value } //ajax('...', msg); return warn.textContent = '用戶信息已成功提交至服務(wù)器'; } submitMsg = submitMsg.before(vld.start.bind(vld)); form.submit.onclick = function(){ submitMsg(); }; //這里只是模擬提交,實際應(yīng)該用form.onsubmit
問題分析
總結(jié)一下易錯的地方還有我敲得時候遇到的問題
Validator.prototype.add = function(domNode, ruleArr){ var self = this; for(var i = 0, rule; rule = ruleArr[i++];){ (function(rule){ var strategyArr = rule.strategy.split(':'), warnMsg = rule.warnMsg; self.rules.push(function(){ var tempArr = strategyArr.concat(); var ruleName = tempArr.shift(); tempArr.unshift(domNode.value); tempArr.push(warnMsg); return vldStrategy[ruleName].apply(domNode, tempArr); }); })(rule); } return this; };
在Validator原型鏈上的add函數(shù)需要注意幾個問題
首先添加IIFE立即執(zhí)行函數(shù)解決閉包問題就不用多說了
函數(shù)內(nèi)又嵌套了函數(shù),導(dǎo)致了this被劫持,所以必須緩存this
var self = this;
最開始我沒有拷貝這個數(shù)組而是直接使用的strategyArr
Validator.prototype.add = function(domNode, ruleArr){ //添加驗證規(guī)則 var self = this; for(var i = 0, rule; rule = ruleArr[i++];){ (function(rule){ var strategyArr = rule.strategy.split(':'), warnMsg = rule.warnMsg; self.rules.push(function(){ // var tempArr = strategyArr.concat(); var ruleName = strategyArr.shift(); strategyArr.unshift(domNode.value); strategyArr.push(warnMsg); return vldStrategy[ruleName].apply(domNode, strategyArr); }); })(rule); } return this; };
第一次提交沒有問題,但再次提交就會報錯
這是因為第一次提交后,閉包中的strategyArr已經(jīng)改變
之后的提交,對這個數(shù)組進(jìn)行操作就不是預(yù)期的結(jié)果了
在這個地方我犯了一個小錯誤,導(dǎo)致我斷點調(diào)試了好長時間 __冏rz
Validator.prototype.start = function(){ //開始驗證表單 for(var i = 0, vldFn; vldFn = this.rules[i++];){ var warnMsg = vldFn(); if(warnMsg){ warn.textContent = warnMsg; return false; } } }
改正前的錯誤代碼是這樣的
Validator.prototype.start = function(){ //開始驗證表單 for(var i = 0, vldFn; vldFn = this.rules[i++];){ var warnMsg = vldFn(); if(warnMsg) warn.textContent = warnMsg; return false; } }
沒錯,只是因為少加了那層大括號
可能是之前只有一行,后來添加return false的時候忘添加了
這里我只是為了簡潔才不寫大括號的
我們平時千萬不要這么寫代碼,簡直挖坑給自己跳
submitMsg = submitMsg.before(vld.start.bind(vld));
添加裝飾者這個地方也要注意
如果不寫bind就會發(fā)生this劫持,同樣會報錯
以上所述是小編給大家介紹的利用策略模式與裝飾模式擴(kuò)展JavaScript表單驗證功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- 從表單校驗看JavaScript策略模式的使用詳解
- JavaScript設(shè)計模式之策略模式實現(xiàn)原理詳解
- javascript設(shè)計模式 – 策略模式原理與用法實例分析
- JavaScript同源策略和跨域訪問實例詳解
- JavaScript設(shè)計模式之策略模式詳解
- javascript設(shè)計模式之策略模式學(xué)習(xí)筆記
- 輕松掌握J(rèn)avaScript策略模式
- 學(xué)習(xí)JavaScript設(shè)計模式之策略模式
- javascript設(shè)計模式--策略模式之輸入驗證
- 學(xué)習(xí)JavaScript設(shè)計模式(策略模式)
- 詳解JavaScript的策略模式編程
- 深入理解JavaScript系列(19):求值策略(Evaluation strategy)詳解
- 深入理解JavaScript系列(33):設(shè)計模式之策略模式詳解
- JavaScript設(shè)計模式之策略模式實例
- 怎樣用Javascript實現(xiàn)策略模式
相關(guān)文章
JS實現(xiàn)"上次操作未完成禁止新操作"邏輯特事特辦方案
這篇文章主要介紹了詳解JS如何實現(xiàn)"上次操作未完成禁止新操作"的邏輯及思路,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05為JavaScript提供睡眠功能(sleep) 自編譯JS引擎
如何在js中讓函數(shù)睡眠多少秒? 經(jīng)常會有Javascript初學(xué)者提出這樣的問題,自從js出現(xiàn)以來.2010-08-08JavaScript通過join函數(shù)連接數(shù)組里所有元素的方法
這篇文章主要介紹了JavaScript通過join函數(shù)連接數(shù)組里所有元素的方法,實例分析了javascript中join函數(shù)的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03