>[success] # 正常思维的表单验证 ~~~ 1.通过if else来进行判断验证,后期会维护大量的if - else的逻辑验证 ~~~ ~~~ <body> <form id='login-form' action="" method="post"> <label for="userName">用户名</label> <input type="text" id="userName" name="userName"> <label for="phoneNumber">手机号</label> <input type="number" id="phoneNumber" name="phoneNumber"> <label for="password">密码</label> <input type="password" id="password" name="password"> <button id='login'>登录</button> </form> </body> <script> var loginForm = document.getElementById('login-form'); loginForm.onsubmit = function () { if(loginForm.userName.value ===''){ alert('用户名不能为空') return false } if(loginForm.password.value.length<6){ alert('密码长度不能小于六位') return false; } if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(loginForm.phoneNumber.value)) { alert('手机号格式错误') return false; } } </script> ~~~ >[success] # 利用策略模式 ~~~ 1.策略模式需要将代码拆成两个两部分,第一个部分'封装不同策略的策略组',也就是 要将这些验证对象封装成策略对象,第二部分需要'Context'执行这些策略的调用,调用策略对象 2.分析第一部分要封装的策略组代码,也就是上面的if else,都需要被判断参数, 报错信息。其中判断长度比较特别需要,一个额外的长度来控制各种长度出现的 情况 3.分析第二部分也就是一个'执行这些策略的调用',构想就是分成两部分,第一部分就是添加 你想要的策略,第二部分就是统一执行你添加的策略 ~~~ >[danger] ##### 封装一个策略对象 ~~~ 1.根据上面分析,整个负责验证的策略对象中的每个策略,最少需要两个参数,第一个参数 就是要验证的值,第二个参数就是验证后的报错提示信息。除了特殊策略,例如长度验证 需要一个关于长度的判断值 2.这里的策略模式使用了js的特性,直接将这些策略方式封装到一个对象即可,如果是后台 例如java这种静态语言,可能需要一个'接口' 一些策略要继承这个接口的策略类 ~~~ * es5 版本 ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form id="registerForm" method="post"> <label for="userName">用户名</label> <input type="text" id="userName" name="userName"> <label for="phoneNumber">手机号</label> <input type="number" id="phoneNumber" name="phoneNumber"> <label for="password">密码</label> <input type="password" id="password" name="password"> <button id='login'>登录</button> </form> </body> <script> /** * 策略模式的两个点 * 1.存所有的验证(策略) * 2.有一个可以调用所有验证(执行策略) * 策略模式执行的类: * 3.有一个方法这个方法是专门又来存这些验证的,存这些验证需要什么 * 3.1 需要一个能存的变量 因为要存多个这个变量选数组 * 3.2 要存的是什么,这里要存的是错误提示 * 3.2.1 要存错误提示怎么存是进来一个就调用把错误提示存进这个变量? * 还是说存这些方法,在最后执行的时候调用这些方法比较好 * 这里选用后者存这些验证方法 * 3.3 能存了核心在哪,在策略模式最后需要有个执行者,执行这些策略,也就是 * 这个类需要一个执行方法start * 4.要细分存储策略的add方法的具体参数,需要有一个要验证的对象,需要有验证的规则,需要有验证的提示 * **/ // 策略对象 const strategies = { isNonEmpty: function (value, errorMsg) { // 不能为空,第一个参数要验证的值,第二个是提示 if(value === ''){ return errorMsg } }, minLength:function (value, length,errorMsg) { // 最小长度 if(value.length < length){ return errorMsg } }, isMobile:function (value,errorMsg) { if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) { // 验证电话号码 return errorMsg } } } // 委托来让 Context 拥有执行策略的能力Validator function Validator() { // 对应第三条的分析 this.cache = [] } // 关于参数对照第四条 Validator.prototype.add = function (dom,rule,errorMsg) { // 规则针对要传入自己额外规则参数的验证 我们规定用':' 分开 // 因此需要对规则在进行拆分 var ary = rule.split(':') // 对规则进一步的验证发方法存放在this.cache中 this.cache.push(function () { // 验证规则 var strategy = ary.shift() // 分析我么的策略中最特殊需要参数的方法function (value, length,errorMsg) // 经过上一步的操作 ary 中只能有两种情况,一种是空数组,一种是有一个自己定义的验证判断规则参数 ary.unshift(dom.value) ary.push(errorMsg) console.log(strategy) return strategies[strategy].apply(dom,ary) }) } // 最后调用策略的方法 Validator.prototype.start = function () { for (var i=0,validatorFunc;validatorFunc = this.cache[i++];){ var msg = validatorFunc() if(msg){ return msg } } } var registerForm = document.getElementById('registerForm') var validataFun = function () { var validator = new Validator() validator.add(registerForm.userName,'isNonEmpty','用户名不能为空') validator.add(registerForm.password,'minLength:6','密码长度不能少于六位') validator.add(registerForm.phoneNumber,'isMobile','手机格式不正确') var errorMsg = validator.start() return errorMsg } registerForm.onsubmit = function (e) { e.preventDefault() var errorMsg = validataFun() if(errorMsg){ alert(errorMsg) return false // 组织表单提交 } } </script> </html> ~~~ * es6 版本 ~~~ 1.下面代码最有意思的核心点是在add方法,打破思维定式数组还可以存取一些方法需要的 时候在调用这些方法得到我们想要的 2.当这些数组方法被执行后,这些数组存的就不是这些方法了,而是这些方法的返回值 ~~~ ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <label for="userName">用户名</label> <input type="text" id="userName" name="userName"> <label for="phoneNumber">手机号</label> <input type="number" id="phoneNumber" name="phoneNumber"> <label for="password">密码</label> <input type="password" id="password" name="password"> <button id='login'>登录</button> </body> <script> // 策略对象 const strategies = { isNonEmpty: function (value, errorMsg) { // 不能为空,第一个参数要验证的值,第二个是提示 if(value === ''){ return errorMsg } }, minLength:function (value, length,errorMsg) { // 最小长度 if(value.length < length){ return errorMsg } }, isMobile:function (value,errorMsg) { if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) { // 验证电话号码 return errorMsg } } } // 执行这些策略的调用的类Validator -- 这里直接es6 写法 class Validator{ constructor(strategies){ this.cache = [] this.strategies = strategies // 策略对象 } // 保存所有验证方法 add(value, rule, errorMsg){ let ary = rule.split(':') this.cache.push(()=>{ let strategy = ary.shift() ary.unshift(value) ary.push(errorMsg) // 当想给函数传多个参数利用数组的形式的话es5 首先考虑利用apply // es6 理解结构赋值的方法this.strategies[strategy](...ary) return this.strategies[strategy].apply(this,ary) }) } // 统一执行调用 start(){ let msg = '' for(let item of this.cache){ msg = item() if(msg){ return msg } } } } let userName = document.getElementById('userName'); let password = document.getElementById('password'); let phoneNumber = document.getElementById('phoneNumber'); // 每次执行调用为了获取最新的输入value值 let validataFun = function () { // 创建一个'执行这些策略的调用' 的Validator实例 let validator = new Validator(strategies) validator.add(userName.value,'isNonEmpty','用户名不能为空') validator.add(password.value,'minLength:6','密码长度不能少于六位') validator.add(phoneNumber.value,'isMobile','手机格式不正确') var errorMsg = validator.start() return errorMsg } document.getElementById('login').onclick = ()=>{ let errorMsg = validataFun() if(errorMsg){ alert(errorMsg) } } </script> </html> ~~~ * 书中的es5 写法