js前端设计模式优化50%表单校验代码示例


Posted in Javascript onJune 21, 2022

表单校验

背景

假设我们正在编写一个注册页面,在点击注册按钮之时,有如下几条校验逻辑:

  • 用户名不能为空
  • 密码长度不能少于6位
  • 手机号码必须符合格式

常规写法:

const form = document.getElementById('registerForm');
form.onsubmit = function () {
  if (form.userName.value === '') {
    alert('用户名不能为空');
    return false;
  }
  if (form.password.value.length < 6) {
    alert('密码长度不能少于6位');
    return false;
  }
  if (!/^1[3|5|8][0-9]{9}$/.test(form.phoneNumber.value)) {
    alert('手机号码格式不正确');
    return false;
  }
  ...
}

这是一种很常见的代码编写方式,但它有许多缺点:

  • onsubmit 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的校验规则。
  • onsubmit 函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长度从6改成8,我们都必须深入 obsubmit 函数的内部实现,这是违反开放-封闭原则的。
  • 算法的复用性差,如果在项目中增加了另外一个表单,这个表单也需要进行一些类似的校验,我们很可能将这些校验逻辑复制得漫天遍野。

如何避免上述缺陷,更优雅地实现表单校验呢?

策略模式介绍

? 策略模式是一种行为设计模式, 它能让你定义一系列算法, 把它们一个个封装起来, 并使它们可以相互替换。

真实世界类比

js前端设计模式优化50%表单校验代码示例

此图源自 refactoringguru.cn/design-patt…

假如你需要前往机场。 你可以选择骑自行车、乘坐大巴或搭出租车。这三种出行策略就是广义上的“算法”,它们都能让你从家里出发到机场。你无需深入它们的内部实现细节,如怎么开大巴、公路系统如何确保你家到机场有通路等。你只需要了解这些策略的各自特点:所需要花费的时间与金钱,你就可以根据预算和时间等因素来选择其中一种策略。

更广义的“算法”

在实际开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装一系列的“业务规则”。只要这些业务规则指向的目标一致,并且可以被替换使用,我们就可以用策略模式来封装它们。

策略模式的组成

一个策略模式至少由两部分组成。

第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。

第二个部分是环境类 Context,Context 接受客户的请求,随后把请求委托给某一个策略类。

利用策略模式改写

定义规则(策略),封装表单校验逻辑:

const strategies = {
  isNonEmpty: function (value, errMsg) {
    if (value === '') {
      return errMsg;
    }
  },
  minLenth: function (value, length, errMsg) {
    if (value.length < length) {
      return errMsg;
    }
  },
  isMobile: function (value, errMsg) {
    if (!/^1[3|5|8][0-9]{9}$/.test(value)) {
      return errMsg;
    }
  }
}

定义环境类 Context,进行表单校验,调用策略:

form.onsubmit = function () {
	const validator = new Validator();
	validator.add(form.userName, 'isNonEmpty', '用户名不能为空');
	validator.add(form.password, 'minLength:6', '密码长度不能少于6位');
	validator.add(form.phoneNumber, 'isMobile', '手机号码格式不正确');
	const errMsg = validator.start();
	if (errMsg) {
		alert(errMsg);
		return false;
	}
}

Validator 类代码如下:

class Validator {
	constructor() {
		this.cache = [];
	}
	add(dom, rule, errMsg) {
		const arr = rule.split(':');
		this.cache.push(() => {
			const strategy = arr.shift();
			arr.unshift(dom.value);
			arr.push(errMsg);
			return strategies[strategy].apply(dom, arr);
		})
	}
	start() {
		for (let i = 0; i < this.cache.length; i++) {
			const msg = this.cache[i]();
			if (msg) return msg;
		}
	}
}

使用策略模式重构代码之后,我们消除了原程序中大片的条件分支语句。我们仅仅通过“配置”的方式就可以完成一个表单校验,这些校验规则也能在程序中任何地方复用,还能作为插件的形式,方便地移植到其他项目中。

策略模式优缺点

优点:

  • 可以有效地避免多重条件选择语句。
  • 开放-封闭原则完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。
  • 可以使算法复用在系统的其他地方,避免许多重复的复制粘贴工作。

缺点:

  • 使用策略模式会在程序中增加许多策略类或策略对象
  • 要使用策略模式,必须了解所有的 strategy,了解它们的不同点,我们才能选择一个合适的 strategy。这是违反最少知识原则的。

策略模式适合应用场景

? 当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。

策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。

? 当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。

策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。

? 如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。

策略模式让你能将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来。 不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。

? 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。

策略模式将所有继承自同样接口的算法抽取到独立类中, 因此不再需要条件语句。 原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。

总结

在上述例子中,使用策略模式虽然使得程序中多了许多策略对象和执行策略的代码。但这些代码可以在应用中任意位置的表单复用,使得整个程序代码量大幅减少,且易维护。下次面对多表单校验的需求时,别再傻傻写一堆 if-else 逻辑啦,快试试策略模式!

更多关于js前端设计模式优化表单校验的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
javascript qq右下角滑出窗口 sheyMsg
Mar 21 Javascript
30个最佳jQuery Lightbox效果插件分享
Apr 11 Javascript
jquery.ui.draggable中文文档(原文翻译)
Nov 15 Javascript
jQuery判断对象是否存在的方法
Feb 05 Javascript
js实现文字跟随鼠标移动而移动的方法
Feb 28 Javascript
jQuery晃动层特效实现方法
Mar 09 Javascript
JS+CSS实现仿支付宝菜单选中效果代码
Sep 25 Javascript
Node.js中JavaScript操作MySQL的常用方法整理
Mar 01 Javascript
完美解决node.js中使用https请求报CERT_UNTRUSTED的问题
Jan 08 Javascript
关于vue单文件中引用路径的处理方法
Jan 08 Javascript
使用gulp构建前端自动化的方法示例
Dec 25 Javascript
Vue实现微信支付功能遇到的坑
Jun 05 Javascript
微前端qiankun改造日渐庞大的项目教程
Jun 21 #Javascript
JavaScript架构localStorage特殊场景下二次封装操作
Jun 21 #Javascript
js前端图片加载异常兜底方案
Jun 21 #Javascript
JavaScript中10个Reduce常用场景技巧
Jun 21 #Javascript
js前端面试常见浏览器缓存强缓存及协商缓存实例
Jun 21 #Javascript
JavaScript前端面试组合函数
Jun 21 #Javascript
Vue2项目中对百度地图的封装使用详解
You might like
PHP脚本的10个技巧(8)
2006/10/09 PHP
php录入页面中动态从数据库中提取数据的实现
2006/10/09 PHP
C# Assembly类访问程序集信息
2009/06/13 PHP
PHP array 的加法操作代码
2010/07/24 PHP
php生成QRcode实例
2014/09/22 PHP
PHP Callable强制指定回调类型的方法
2016/08/30 PHP
详解PHP实现支付宝小程序用户授权的工具类
2018/12/25 PHP
javascript实现动态增加删除表格行(兼容IE/FF)
2007/04/02 Javascript
javascript 放大镜 v1.0 基于Yui2 实现的放大镜效果
2010/03/08 Javascript
asp.net+js 实现无刷新上传解析csv文件的代码
2010/05/17 Javascript
JavaScript基础篇之变量作用域、传值、传址的简单介绍与实例
2013/06/29 Javascript
js根据鼠标移动速度背景图片自动旋转的方法
2015/02/28 Javascript
JS实现按比例缩放图片的方法(附C#版代码)
2015/12/08 Javascript
使用snowfall.jquery.js实现爱心满屏飞的效果
2017/01/05 Javascript
Angularjs 事件指令详细整理
2017/07/27 Javascript
解决html-jquery/js引用外部图片时遇到看不了或出现403的问题
2017/09/22 jQuery
jQuery 禁止表单用户名、密码自动填充功能
2017/10/30 jQuery
解决vue中虚拟dom,无法实时更新的问题
2018/09/15 Javascript
JS中数组实现代码(倒序遍历数组,数组连接字符串)
2019/12/29 Javascript
js实现div色块拖动录制
2020/01/16 Javascript
vue随机验证码组件的封装实现
2020/02/19 Javascript
element el-tree组件的动态加载、新增、更新节点的实现
2020/02/27 Javascript
[03:28]2014DOTA2国际邀请赛 走近EG战队天才中单Arteezy
2014/07/12 DOTA
在Apache服务器上同时运行多个Django程序的方法
2015/07/22 Python
Python实现并行抓取整站40万条房价数据(可更换抓取城市)
2016/12/14 Python
Python字符串的一些操作方法总结
2019/06/10 Python
使用CSS3中的calc()属性来以算式表达尺寸数值
2016/06/06 HTML / CSS
HTML5是否真的可以取代Flash
2010/02/10 HTML / CSS
意大利消费电子产品购物网站:SLG Store
2019/12/26 全球购物
意大利包包和行李箱销售网站:Bagaglio.it
2021/03/02 全球购物
求职信范文大全
2014/05/26 职场文书
交通事故赔偿协议书怎么写
2014/10/04 职场文书
2015欢度元旦标语口号
2014/12/09 职场文书
职代会闭幕词
2015/01/28 职场文书
人事任命通知书
2015/04/21 职场文书
未婚证明格式
2015/06/15 职场文书