JS前端中的设计模式和使用场景示例详解

 更新时间:2022年8月10日 13:06  点击:344 作者:35岁就退休的老狗

引言

相信大家在日常学习和工作中都多多少少听说/了解/使用过 设计模式,我们都知道,使用恰当的设计模式可以优化我们的代码,那你是否知道对于前端开发哪些 设计模式 是日常工作经常用到或者必须掌握的呢?本文我将带大家一起学习下前端常见的设计模式以及它们的 使用场景!!!

本文主讲:

  • 策略模式
  • 代理模式

适合人群:

  • 前端人员
  • 设计模式小白/想知道如何在项目中使用设计模式

策略模式

策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。从定义不难看出,策略模式是用来解决那些一个功能有多种方案、根据不同条件输出不同结果且条件很多的场景,而这些场景在我们工作中也经常遇到,接下来我将用几个例子来展示策略模式在哪里用以及如何用。

1.绩效考核

假如我们有这么一个需求,需要根据员工的绩效考核给员工发放年终奖(分为A/B/C/D四个等级 分别对应基础奖金的1/2/3/4倍),我们很容易就写出这样的代码

//level 评级 basicBonus 基础奖金
const computeBonus(level, basicBonus) = () => {
	if(level === 'A') {
        return basicBonus * 1;
    } else if(level === 'B') {
        return basicBonus * 2;
    } else if(level === 'C') {
        return basicBonus * 3;
    } else if(level === 'D') {
        return basicBonus * 4;
    }
}
computeBonus('A', 1000);//1000

我们发现,以上的代码可以轻松实现我们的需求,但是这些代码存在什么问题呢?

  • computedBonus方法十分臃肿,包含太多if-else
  • 拓展性差,后续如果想要更改评级或者规则都需要进入该函数内部调整。
  • 复用性差。

那策略模式是怎么解决这些问题的呢?我们都知道,设计模式的核心之一就是将可变的和不可变的部分抽离分装,那我们根据这个原则来修改我们的代码,其中可变的就是如何使用这些算法(多少个评级),不变的是算法的内容(评级对应的奖金),下面就是改变后的代码

//定义策略类
const strategies = {
    'A': function(basicBonus) {
        return basicBonus * 1;
    },
    'B': function(basicBonus) {
        return basicBonus * 2;
    },
    'C': function(basicBonus) {
        return basicBonus * 3;
    },
    'D': function(basicBonus) {
        return basicBonus * 4;
    },
}
//使用策略类
const computeBonus = (level, basicBonus) {
    return strategies[level](basicBonus);
}
computeBouns('A', 1000);//1000

从上面可以看出,我们将每种情况都单独弄成一个策略,然后根据传入评级和奖金计算年终奖,这样我们的computeBonus方法代码量大大减少,也不用冗杂的if-else分支,同时,如果我们想要改变规则,只需要在strategies中添加对应的策略,增加了代码的健壮性

2.表单验证

我们日常的工作中,不可避免地需要做表单相关的业务,毕竟这是前端最初始的职能之一。而表单绕不开表单验证,那接下来我将带大家看看策略模式在表单中如何使用。

需求: 假设我们有一个登录业务,提交表单前需要做验证,验证规则如下:1.用户名称不能为空,2.密码不能少于6位,3.手机格式要正确。

我们很容易写出以下代码

const verifyForm = (formData) => {
    if(formData.userName == '') {
        console.log('用户名不能为空');
        return false
    };
    if(formData.password.length < 6) {
        console.log('密码长度不能小于6位');
        return false;
    }
    if(( !/(^1[3|5|8][0-9]{9}$)/.test(formData.phone)) {
       console.log('手机格式错误');
    	return false
       }
}

显然,这样也可以完成表单校验的功能,但是这样写同样存在着上面说的问题,接下来,我们看下用策略模式如何改写

//编写策略对象
const strategies = {
	isEmpty: function(value, error) {
        if(value === '' {
           return error;
           })
    },
    minLength: function(value, len, error) {
        if(value.length < len {
           return error;
           })
    },
    isPhone: function(value, error) {
        if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){ 
 			return errorMsg; 
 			}
    };
}
//接下来我们编写实现类 用于生成对应的策略实例
class Validator {
    controustor(cache) {
        this.cache = cache || []; //保存校验规则
    };
    add(dom, rule, error) {
        const arr = rule.splt(':');//分离参数
        this.cache.push(function(){ // 把校验的步骤用空函数包装起来,并且放入 cache 
 		const strategy = arr.shift(); // 用户挑选的 strategy 
 		arr.unshift( dom.value ); // 把 input 的 value 添加进参数列表
 		arr.push( errorMsg ); // 把 error 添加进参数列表
 		return strategies[ strategy ].apply( dom, ary ); 
 	});
    };
    start() {
        for ( let i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ 
 			var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息
 				if ( msg ){ // 如果有确切的返回值,说明校验没有通过
 							return msg; 
 							} 
 				}
    }
}
//编写完策略对象和实例类后我们就可以看看如何使用了
const validataFunc = function(){ 
 let validator = new Validator(); // 创建一个 validator 对象
 /***************添加一些校验规则****************/ 
 validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' ); 
 validator.add( registerForm.password, 'minLength:6', '密码长度不能少于 6 位' ); 
 validator.add( registerForm.phoneNumber, 'isMobile', '手机号码格式不正确' ); 
 var errorMsg = validator.start(); // 获得校验结果
 return errorMsg; // 返回校验结果
} 
 var registerForm = document.getElementById( 'registerForm' ); 
 registerForm.onsubmit = function(){ 
 var errorMsg = validataFunc(); // 如果 errorMsg 有确切的返回值,说明未通过校验
 if ( errorMsg ){ 
 alert ( errorMsg ); 
 return false; // 阻止表单提交
 } 
}; 

这样,我们就用策略模式将需求改好了,之后如果我们的校验规则改变了,修改起来也是很方便的,比如:

validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' ); // 改成:

validator.add( registerForm.userName, 'minLength:10', '用户名长度不能小于 10 位' );

而且,我们也可以给文本框添加多个校验规则,只需要修改下策略对象以及策略方法即可!大大地增强了代码地健壮性。

策略模式的优缺点:

优点:

  • 避免多重条件选择语句(if-else
  • 具有可拓展性,可独立抽离封装,避免重复复制粘贴

缺点:

  • 增加很多策略类或者策略对象,但是这其实不算什么大缺点
  • 比起直接编写业务代码需要思考策略对象以及其他细节

代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。代理模式应该是我们日常用到比较多的设计模式了(我们日常工作中不知不觉就会用到代理模式,可能你没发现而已)。

代理模式分为保护代理(用于控制不同权限的对象对目标对象的访问)和虚拟代理(把开销很大的对象或者操作延迟到真正需要的时候再去创建 类比引入时动态引入)两种,但是前端基本不用到保护代理,或者说很难实现保护代理,所以大部分情况下我们用的都是虚拟代理,接下来我主要也是讲虚拟代理!

举个例子,加入A想要给C送情书,但是A没有直接把情书交给C,而是让B代为传送情书,那么B就是代理,他的职责就是替A做事,这个就是最简单的代理模式,接下来我们还是老样子,边写需求边讲解

1.图片懒加载:

相信大家对于图片懒加载都不陌生吧,他可以在我们加载出目标图片前预加载占位图片,避免空白区域影响体验,那我们很容易就能写出下面的代码

const lazyImage = (function() {
    let imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    let image = new Image;
    image.onload = function() {
        imgNode.src = image.src;//在这里设置图片的真正路由
    };
    return {
        setSrc: function(src) {
            imgNode.src = '....'//预加载本地图片;
            image.src = src
        }
    }
})()
lazyImage.setSrc('https://olddog.jpg');//加载真正的图片

我们看上面的代码,也可以完成预加载的功能,但是这样的代码存在着什么样的问题呢

  • 违反了单一职责原则,而且耦合度太高,如果后期我们不需要懒加载了,或者需要根据判断条件判断是否懒加载,就不得不去动lazyImage的代码

接下来,我们就用代理模式来改写一下这个例子

const lazyImage = (function() {
    let imageNode = document.createElement('img');
    document.body.appendChild(imageNode);
    return {
        setSrc: function(src) {
            imageNode.src = src;//设置目标src
        }
    }
})()
//代理函数
const proxyImage = (function() {
    let image = new Image;
    image.onload = function() {
        myImage.setSrc(this.src);
    }
    return {
        setSrc: function(src) {
            myImage.setSrc('....')//预加载本地图片
            img.src = src
        }
    }
})()
proxyImage.setSrc('https://olddog.jpg');//使用代理加载

我们观察用代理模式写的代码,发现我们将预加载的逻辑转移到了代理函数中,这样有啥好处呢

  • 如果后期不需要预加载了,只需要取消代理,即将proxyImage.setSrc(...)改成lazyImage.setSrc(...)
  • 代理函数的使用方式和原函数一模一样,使用者不需要知道代理的实现细节也能使用

不知道大家有没有发现,代理函数和原函数有一部分相似的逻辑和操作,只是代理函数的功能更多,这其实也是代理模式的特征之一,代理函数在保证实现原函数的基本功能的前提下实现更多功能,这样即使使用者不清楚逻辑也能直接使用,而且后期改动成本很低,只需要改回原函数的使用即可!!

2.缓存代理

设想一下,如果现在要你写一个简单的求积函数,你会怎么写

const mult = function() {
	let result = 1;
    for(let i = 0, len = arguments.length; i < len; i++) {
        result *= arguments[i];
    }
    return result
}
mult(1, 2, 3);//6

我们来看一下上面的代码有啥缺点,上面的代码虽然实现了求积,但是如果我们mult(1,2,3)之后再去mult(1,2,3),那么系统还是会再计算一遍,这样无疑是性能浪费,那么我们就能用代理模式来改写:

const proxyMult = (function() {
	let cache = {};//缓存计算结果
	return function() {
		const args = Array.prototype.join.call( arguments, ',');
        if(args in cache) {
            return cache[args]
        }
        return cache[args] = mult.apply(this.arguments)
	}
})();
proxyMult(1,2,3);//6
proxyMult(1, 2, 3);//输出6 但是不会重新计算

可以看到,我们用代理模式改写后避免了重复运算的浪费,这只是一种情景,还有其他相似情景,比如我们分页请求数据,可以使用相似的思路,避免对同页的数据重复请求,这在工作中非常有用!!

总结

我们日常工作中还有很多地方用到代理,比如代理合并请求(间断性合并而不是全部合并,减少服务器压力)、惰性加载或创建申请资源等等,而什么时候使用代理其实不需要提前花很多精力去思考,当我们写着写着发现可以抽离使用代理模式的时候再去使用也不迟。由于文章篇幅有限,本文就先讲解策略模式和代理模式,后续将继续更新其他实用的设计模式,喜欢的小伙伴可以点个赞和关注一下,有啥问题可以评论区一起学习交流!

前端设计模式之发布-订阅模式

以上就是JS前端中的设计模式和使用场景示例详解的详细内容,更多关于前端设计模式场景的资料请关注猪先飞其它相关文章!

原文出处:https://juejin.cn/post/7128965885057826846

[!--infotagslink--]

相关文章

  • JavaScript判断浏览器及其版本信息

    本篇文章主要分享了通过window.navigator来判断浏览器及其版本信息的实例代码。具有一定的参考价值,下面跟着小编一起来看下吧...2017-01-23
  • js实现浏览器打印功能的示例代码

    这篇文章主要介绍了js如何实现浏览器打印功能,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-15
  • 利用JS实现点击按钮后图片自动切换的简单方法

    下面小编就为大家带来一篇利用JS实现点击按钮后图片自动切换的简单方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-10-25
  • 学习JavaScript设计模式之装饰者模式

    这篇文章主要为大家介绍了JavaScript设计模式中的装饰者模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
  • javascript设计模式之解释器模式详解

    神马是“解释器模式”?先翻开《GOF》看看Definition:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在开篇之前还是要科普几个概念: 抽象语法树: 解释器模式并未解释如...2014-06-07
  • Nest.js参数校验和自定义返回数据格式详解

    这篇文章主要给大家介绍了关于Nest.js参数校验和自定义返回数据格式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-28
  • 详解前端安全之JavaScript防http劫持与XSS

    作为前端,一直以来都知道HTTP劫持与XSS跨站脚本、CSRF跨站请求伪造。防御这些劫持最好的方法是从后端入手,前端能做的太少。而且由于源码的暴露,攻击者很容易绕过防御手段。但这不代表我们去了解这块的相关知识是没意义的,本文的许多方法,用在其他方面也是大有作用。...2021-05-24
  • JavaScript仿支付宝密码输入框

    那么今天我就用JavaScript代码来实现这个效果吧,那么首先介绍一下整个的思路,首先我们先将确定输入密码的位数,我的需求是5位,那么就用一个div标签包住5个input标签...2016-01-02
  • js+css实现回到顶部按钮(back to top)

    这篇文章主要为大家详细介绍了js+css实现回到顶部按钮back to top回到顶部按钮,感兴趣的小伙伴们可以参考一下...2016-03-03
  • js实现上传图片及时预览

    这篇文章主要为大家详细介绍了js实现上传图片及时预览的相关资料,具有一定的参考价值,感兴趣的朋友可以参考一下...2016-05-09
  • js实现调用网络摄像头及常见错误处理

    这篇文章主要介绍了js实现调用网络摄像头及常见错误处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-07
  • JS实现随机生成验证码

    这篇文章主要为大家详细介绍了JS实现随机生成验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-06
  • 一个关于JS正则匹配的踩坑记录

    这篇文章主要给大家介绍了一个关于JS正则匹配的踩坑记录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-13
  • js组件SlotMachine实现图片切换效果制作抽奖系统

    这篇文章主要介绍了js组件SlotMachine实现图片切换效果制作抽奖系统的相关资料,需要的朋友可以参考下...2016-04-19
  • vue.js 表格分页ajax 异步加载数据

    Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.这篇文章主要介绍了vue.js 表格分页ajax 异步加载数据的相关资料,需要的朋友可以参考下...2016-10-20
  • 基于JavaScript实现表单密码的隐藏和显示出来

    为了网站的安全性,很多朋友都把密码设的比较复杂,但是如何密码不能明显示,不知道输的是对是错,为了安全起见可以把密码显示的,那么基于js代码如何实现的呢?下面通过本文给大家介绍JavaScript实现表单密码的隐藏和显示,需要的朋友参考下...2016-03-03
  • js实现列表按字母排序

    这篇文章主要为大家详细介绍了js实现列表按字母排序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-11
  • 如何使用JavaScript实现无缝滚动自动播放轮播图效果

    这篇文章主要介绍了如何使用JavaScript实现“无缝滚动 自动播放”轮播图效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-08-20
  • 基于JavaScript实现文字超出部分隐藏

    这篇文章主要介绍了基于JavaScript实现文字超出部分隐藏 的相关资料,需要的朋友可以参考下...2016-03-01
  • JS实现响应鼠标点击动画渐变弹出层效果代码

    这篇文章主要介绍了JS实现响应鼠标点击动画渐变弹出层效果代码,具有非常自然流畅的动画过度效果,涉及JavaScript针对鼠标事件的响应及页面元素样式的动态操作相关技巧,需要的朋友可以参考下...2016-03-28