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和JQuery实用代码片段(一)
Apr 07 Javascript
页面实时更新时间的JS实例代码
Dec 18 Javascript
JS应用正则表达式转换大小写示例
Sep 18 Javascript
JS+CSS实现的日本门户网站经典选项卡导航效果
Sep 27 Javascript
JavaScript中创建对象的模式汇总
Apr 19 Javascript
Angular中ng-options下拉数据默认值的设定方法
Jun 21 Javascript
如何在vue中使用ts的示例代码
Feb 28 Javascript
JavaScript类型相关的常用操作总结
Feb 14 Javascript
vue2.0结合Element-ui实战案例
Mar 06 Javascript
vue.js+elementUI实现点击左右箭头切换头像功能(类似轮播图效果)
Sep 05 Javascript
使用layui+ajax实现简单的菜单权限管理及排序的方法
Sep 10 Javascript
Vue使用screenfull实现全屏效果
Sep 17 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+APACHE实现用户论证的方法
2006/10/09 PHP
php随机显示图片的简单示例
2014/02/15 PHP
PHP设计模式之简单工厂和工厂模式实例分析
2019/03/25 PHP
ThinkPHP5.1+Ajax实现的无刷新分页功能示例
2020/02/10 PHP
PHP接口类(interface)的定义、特点和应用示例
2020/05/18 PHP
js 动态添加标签(新增一行,其实很简单,就是几个函数的应用)
2009/03/26 Javascript
js 匿名调用实现代码
2009/06/19 Javascript
JavaScript 保存数组到Cookie的代码
2010/04/14 Javascript
JS命名空间的另一种实现
2013/08/09 Javascript
Chrome扩展页面动态绑定JS事件提示错误
2014/02/11 Javascript
JS 获取鼠标左右键的键值方法
2014/10/11 Javascript
JS解析XML文件和XML字符串详解
2015/04/17 Javascript
jQuery实现高亮显示网页关键词的方法
2015/08/07 Javascript
jQuery+css实现的tab切换标签(兼容各浏览器)
2016/01/28 Javascript
BootStrap的弹出框(Popover)支持鼠标移到弹出层上弹窗层不隐藏的原因及解决办法
2016/04/03 Javascript
在Docker快速部署Node.js应用的详细步骤
2016/09/02 Javascript
详解vue-cli快速构建项目以及引入bootstrap、jq
2017/05/26 Javascript
Vue-cli@3.0 插件系统简析
2018/09/05 Javascript
vue webpack打包后图片路径错误的完美解决方法
2018/12/07 Javascript
[46:14]完美世界DOTA2联赛PWL S3 Magma vs INK ICE 第一场 12.11
2020/12/16 DOTA
Python中给List添加元素的4种方法分享
2014/11/28 Python
Python备份目录及目录下的全部内容的实现方法
2016/06/12 Python
python flask实现分页效果
2017/06/27 Python
pyqt5让图片自适应QLabel大小上以及移除已显示的图片方法
2019/06/21 Python
Python面向对象之Web静态服务器
2019/09/03 Python
Python字符串大小写转换拼接删除空白
2019/09/19 Python
python实现简易淘宝购物
2019/11/22 Python
Pytorch 神经网络—自定义数据集上实现教程
2020/01/07 Python
全方位了解CSS3的Regions扩展
2015/08/07 HTML / CSS
美国餐厅用品和厨房设备批发网站:KaTom Restaurant Supply
2018/01/27 全球购物
微软巴西官方网站:Microsoft Brasil
2019/09/26 全球购物
售后服务经理岗位职责范本
2014/02/22 职场文书
关于晚自习早退的检讨书
2014/09/13 职场文书
不服从公司安排检讨书
2014/09/24 职场文书
中学生的1000字检讨书
2014/10/11 职场文书
学雷锋活动简报
2015/07/20 职场文书