八大最常用的JavaScript设计模式 js有哪些设计模式
suiw9 2024-10-23 18:47 32 浏览 0 评论
设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的 高内聚 和 低耦合。通俗一点讲的话 打比方面试官经常会问你如何让代码有健壮性。其实把代码中的变与不变分离,确保变化的部分灵活、不变的部分稳定,这样的封装变化就是代码健壮性的关键。而设计模式的出现,就是帮我们写出这样的代码。 设计模式就是解决编程里某类问题的通用模板,总结出来的代码套路就是设计模式。本文章总结下JS在工作中常用的设计模式 ,以帮助大家提高代码性能,增强工作效率!
JavaScript设计模式(一)装饰器模式
圣诞节要到了,许多家庭会买一颗松树装上彩灯,一闪一闪亮晶晶然后摇身一变成了圣诞树。这里 的彩灯就是装饰器,他不会对松树原有的功能产生影响。(还是本来的树)
这种给对象动态地增加职责的方式称为装 饰器(decorator)模式。装饰器模式能够在不改 变对象自身的基础上,在程序运行期间给对象 动态地添加职责。
应用
当我们接手老代码时,需要对它已有的功能做个拓展。
``
var horribleCode = function(){
console.log(’我是一堆你看不懂的老逻辑')
}
// 改成:
var horribleCode = function(){
console.log('我是一堆你看不懂的老逻辑')
console.log('我是新的逻辑')
}
这样做有很多的问题。直接去修改已有的函数体,违背了我们的“开放封闭原则”;往一个函数体里塞这么多逻辑,违背了我们的“单一职责原则”。
为了不被已有的业务逻辑干扰,将旧逻辑与新逻辑分离,把旧逻辑抽出去:
var horribleCode = function(){
console.log(’我是一堆你看不懂的老逻辑')
}
var _horribleCode = horribleCode
horribleCode = function() {
_horribleCode()
console.log('我是新的逻辑')
}
horribleCode()
这样就完成了旧代码的运用 以及新代码的无伤添加了!!!
JavaScript设计模式(二)工厂模式
先来理解一个概念 —— 构造器模式
你开了家动物园,只有两只动物,你可能会这样录入系统:
const monkey = {
name: '悟空',
age: '1'
}
const tiger = {
name: '泰格伍兹',
age: '3'
}
如果你的动物越来越多,对象字面量也会越来越多,这个时候构造函数可以自动创建动物对象
this.name = name
this.age = age
}
const animal = new Animal(name, age) //Animal 就是一个构造器
像 Animal 这样当新建对象的内存被分配后,用来初始化该对象的特殊函数,就叫做构造器。在 JavaScript 中,我们使用构造函数去初始化对象,就是应用了构造器模式。
可以看出每个实例化后 对象( animal )属性的key (name,age) 是不变的,对应的value(空空,泰格伍兹)是变的。所以构造器将赋值过程封装,确保了每个对象属性固定,开放了取值确保个性灵活。
简单工厂模式
动物园要求根据每个动物的食性喜好来分配不同的食物。这样之前封装的Animal 就不能直接用了,我们重新封装的构造器。
this.name = name
this.age = age
this.favorite = 'fruit'
this.food = [apple, banaba]
}
function Carnivore (name,age) {
this.name = name
this.age = age
this.favorite = 'meat'
this.food = [beef, pork]
}
根据喜好可以分配相应的
function Factory(name, age, favorite) {
switch(career) {
case 'fruit':
return new Vegetarian(name, age)
break
case 'meat':
return new Carnivore(name, age)
break
...
}
总结
工厂模式:将创建对象的过程单独封装。
应用场景:有构造函数的地方、写了大量的构造函数、调用了大量的 new的情况下
JavaScript设计模式(三)单例模式
单例模式
保证仅有一个实例,并提供一个访问它的全局访问点,这样的模式就叫做单例模式。然后性能得到优化!
以下代码我们做一个弹窗 如果实例已经创建过 就无需再次创建 这就是单例!
<body>
<input type="button" id="btn1" value="成功">
<input type="button" id="btn2" value="失败">
<input type="button" id="btn3" value="警告">
</body>
<script>
const obj = {
init:function(){
this.ele = document.createElement("dialog"),
document.body.appendChild(this.ele);
},
show:function(c, t){
// 每次显示之前先判断是否已经存在弹出框元素,如果不存在就创建,如果已经存在就不用重新创建,只需要修改样式和显示即可
if(!this.ele){
this.init();
}
this.ele.style.borderColor = c;
this.ele.style.color = c;
this.ele.innerHTML = t;
this.ele.style.display = "block";
clearTimeout(this.t);
this.t = setTimeout(()=>{
this.hide();
}, 2000);
},
hide:function(){
this.ele.style.display = "none";
}
}
const obtn1 = document.getElementById("btn1")
const obtn2 = document.getElementById("btn2")
const obtn3 = document.getElementById("btn3")
obtn1.onclick = function(){
obj.show("green", "成功");
}
obtn2.onclick = function(){
obj.show("red", "失败");
}
obtn3.onclick = function(){
obj.show("yellow", "警告");
}
</script>
总结
优点:适用于单一对象,只生成一个对象实例,避免频繁创建和销毁实例,减少内存占用。
缺点:不适用动态扩展对象,或需创建多个相似对象的场景。
JavaScript设计模式(四)-适配器模式
当电脑需要外接显示器的时候,我们都会用到下面这个东西。转换器帮助我们在不用更改笔记本接口的同时可以适配HDMI。
将转换器抽象到代码层面就是今天要介绍的适配器了。
适配器模式的作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本 由于接口不兼容而不能工作的两个软件实体可以一起工作。
适配器模式(结构型)
应用举例: 点外卖的时候有美团,饿了么可以选择,同一家店如果要对比两个平台的价格来回切换App十分不方便,作为一个Coder能用代码解决的坚决不用人力。这个时候我们就想到写个小应用对比两家的价格。
在他们openapi里找到了对应的方法,发现请求不一样,入参不一样,返回的数据结构也不一样。翻译成伪代码就是如下的状态
class Eleme() {
getElePice() {
console.log('在饿了么上商品的价格')
return {elePrice:xx}
}
}
class Meituan() {
getMeiPice() {
console.log('在美团上商品的价格')
return {meiPrice:xx}
}
}
试想一下,如果再多增加一些其他平台,前端渲染的时候要写多少个if else去判断来源。这个时候我们可以通过引入适配器
class ElemeAdapter() {
getPrice () {
const e = new Eleme()
return { price:e.elePrice}
}
}
class MeituanAdapter() {
getPrice () {
const m = new Meituan()
return { price:m.meiPrice}
}
}
//通过适配器拿到的数据格式都是统一的 {price:xx}
//同样,入参也可以在适配器中统一处理
虽然这种模式很简单,但还有很多场景运用到了适配器模式。如axios抹平了web和node环境下api的调用差异、React的高阶组件等。适配器不会去改变实现层,那不属于它的职责范围,它干涉了抽象的过程。外部接口的适配能够让同一个方法适用于多种系统。
总结
适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎样实 现的,也不考虑它们将来可能会如何演化。适配器模式不需要改变已有的接口,就能够 使它们协同作用。
JavaScript设计模式(四)-适配器模式
代理,顾名思义就是帮助别人做事,GoF对代理模式的定义如下:
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。
代理模式使得代理对象控制具体对象的引用。代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西。
// 我们来举一个简单的例子,假如dudu要送酸奶小妹玫瑰花,却不知道她的联系方式或者不好意思,想委托大叔去送这些玫瑰,
// 那大叔就是个代理(其实挺好的,可以扣几朵给媳妇),那我们如何来做呢?
// 先声明美女对象
var girl = function (name) {
this.name = name;
};
// 这是dudu
var dudu = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
alert("Hi " + girl.name + ", dudu送你一个礼物:" + gift);
}
};
// 大叔是代理
var proxyTom = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
(new dudu(girl)).sendGift(gift); // 替dudu送花咯
}
};
调用
var proxy = new proxyTom(new girl("酸奶小妹"));
proxy.sendGift("999朵玫瑰");
远程代理,也就是为了一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实,就像web service里的代理类一样。
虚拟代理,根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,比如浏览器的渲染的时候先显示问题,
而图片可以慢慢显示(就是通过虚拟代理代替了真实的图片,此时虚 拟代理保存了真实图片的路径和尺寸。
安全代理,用来控制真实对象访问时的权限,一般用于对象应该有不同的访问权限。
JavaScript设计模式(五)-发布订阅模式
今年非常火爆的苹果13, 非常火爆。我每天都会去亚马逊上看看货到没,可他一直处在无货状态,如果他十年不上线,难道我要十年如一日的去看吗。好在亚马逊提供了一个 到货通知 的按钮,订阅到货通知后,只要健身环一到,就会发信息告诉我。
上述就是一个现实中的发布-订阅者模式。我和其他同样想买健身环的买家都属于 订阅者,我们订阅了到货消息,亚马逊作为发布者,当货物到达时会给我们发布货物到货信息。
发布—订阅模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
发布—订阅模式 (行为型)
实现了一个最简单的发布—订阅模式
例子
//发布者 亚马逊
class Publisher() {
construct() {
this.observers = []
}
//添加订阅者
add(oberver) {
this.observers.push(observer)
}
// 通知所有订阅者
notify() {
this.observers.forEach((observer) => {
//调用订阅者的函数
observer.update(this)
})
}
}
// 订阅者类 顾客
class Observer {
constructor() {
}
update() {
console.log('Observer buy buy buy')
}
}
const cunstomer = new CunstomerObserver() //创建订阅者:顾客
const amazon = new Publisher() / /亚马逊
amazon.add(cunstomer) //订阅到货小修消息
amazon.notify() //货物到达通知顾客
JavaScript设计模式(六)-策略模式
策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。
策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。策略的创建由工厂类来完成,封装策略创建的细节。策略模式包含一组策略可选,客户端代码选择使用哪个策略,有两种确定方法:编译时静态确定和运行时动态确定。其中,“运行时动态确定”才是策略模式最典型的应用场景。
大家看这段代码可以看见这里有一堆的if,随着组件的增多if变得庞大难以维护。通过今天的策略模式我们的代码可以大瘦身!
调用
function initNewDataList (avaliable = []) {
avaliable.forEach( (item, index) => {
if (item.type === 'singleBanner') {
//加载组件
}
if (item.type === 'groupBanner') {
//加载组件
}
if (item.type === 'tab') {
//加载组件
}
})
}
改造
function singleBannerFunc (item,index) {
//加载组件
}
function groupBannerFunc (item,index) {
// 加载组件
}
function tabFunc (item,index) {
// 加载组件
}
function initNewDataList (avaliable = []) {
avaliable.forEach( (item, index) =>
{
if (item.type === 'singleBanner') {
singleBannerFunc()
}
if (item.type === 'groupBanner') {
groupBannerFunc()
}
if (item.type === 'tab') {
tabFunc()
}
})
}
总结
策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
优点
- 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它 们易于切换,易于理解,易于扩展。
- 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
- 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻 便的替代方案。
缺点:
使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的 逻辑堆砌在 Context 中要好。
JavaScript设计模式(六)-外观模式整合调用
- 定义
为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使子系统更加容易使用
- 核心
可以通过请求外观接口来达到访问子系统,也可以选择越过外观来直接访问子系统
- 实现
外观模式在JS中,可以认为是一组函数的集合
调用
// 三个处理函数
function start() {
console.log('start');
}
function doing() {
console.log('doing');
}
function end() {
console.log('end');
}
// 外观函数,将一些处理统一起来,方便调用
function execute() {
start();
doing();
end();
}
// 调用init开始执行
function init() {
// 此处直接调用了高层函数,也可以选择越过它直接调用相关的函数
execute();
}
init(); // start doing end
JavaScript设计模式(七)-多态模式
多态
熟悉java的朋友知道,java三大特征之一就有多态,多态给java带来了很大的灵活性,很多设计模式也是通过多态来实现,java中的多态涉及到向上转型和向下转型,而javascript(以下简称js)的"多态"就相对来说容易实现
我们来看一段“多态”的js代码
多态就是可以让函数一个函数根据不同的传参有不同的返回值或者不同的执行过程,让函数更加灵活!!!
JS中可以根据argumengts的特性进行 同一函数返回不同的值或者不同的执行过程实现多态模式!
示例代码
<script>
function Person() {
this.test1 = function () {
if (arguments.length == 1) {
this.show1(arguments[0]);
} else if (arguments.length == 2) {
this.show2(arguments[0], arguments[1]);
} else if (arguments.length == 3) {
this.show3(arguments[0], arguments[1], arguments[2]);
}
};
this.show1 = function (a) {
window.alert("show1()被调用" + a);
};
this.show2 = function (a, b) {
window.alert("show2()被调用" + "--" + a + "--" + b);
};
function show3(a, b, c) {
window.alert("show3()被调用");
}
}
var p1 = new Person();
p1.test1("a", "b");
p1.test1("a");
</script>
JavaScript设计模式(八)-迭代器模式
迭代器模式也叫游标模式,它用来遍历集合对象。这里说的“集合对象”,我们也可以叫“容器”“聚合对象”,实际上就是包含一组对象的对象,比如,数组、链表、树、图、跳表。迭代器模式主要作用是解耦容器代码和遍历代码。大部分编程语言都提供了现成的迭代器可以使用,我们不需要从零开始开发。
迭代器模式**:指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
// jQuery 中的迭代器模式
$.each([1, 2, 3], function(i, n) {
console.log('当前下标为:' + i)
console.log('当前的值为:' + n)
})
外部迭代器
const Iterator = function(obj) {
let current = 0;
const next = function() {
current += 1
}
const isDone = function() {
return current >= obj.length
}
const getCurrentItem = function() {
return obj[ current ]
}
return {
next,
isDone,
getCurrentItem,
length: obj.length
}
}
// 比较两个数组的元素是否相等
const compare = function(iterator1, iterator2) {
if (iterator1.length !== iterator2.length) return false
whilte(!iterator1.isDone() && !iterator2.isDone()) {
if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) return false
// 迭代
iterator1.next()
iterator2.next()
}
// 相等
return true
}
迭代器模式是一种相对简单的模式,简单到很多时候我们都不认为它是一种设计模式。大部分语言都内置有迭代器模式。
总结
设计模式是为了可复用、可拓展、高性能软件,前人给我们总结的宝贵经验。
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
当然,软件设计模式只是一个引导,在实际的软件开发中,必须根据具体的需求来选择
总结
发布—订阅模式的优点: 时间上的解耦,对象之间的解耦
相关推荐
- 5款Syslog集中系统日志常用工具对比推荐
-
一、为何要集中管理Syslog?Syslog由Linux/Unix系统及其他网络设备生成,广泛分布于整个网络。因其包含关键信息,可用于识别网络中的恶意活动,所以必须对其进行持续监控。将Sys...
- 跨平台、多数据库支持的开源数据库管理工具——DBeaver
-
简介今天给大家推荐一个开源的数据库管理工具——DBeaver。它支持多种数据库系统,包括Mysql、Oracle、PostgreSQL、SLQLite、SQLServer等。DBeaver的界面友好...
- 强烈推荐!数据库管理工具:Navicat Premium 16.3.2 (64位)
-
NavicatPremium,一款集数据迁移、数据库管理、SQL/查询编辑、智能设计、高效协作于一体的全能数据库开发工具。无论你是MySQL、MariaDB、MongoDB、SQLServer、O...
- 3 年 Java 程序员还玩不转 MongoDB,网友:失望
-
一、什么场景使用MongoDB?...
- 拯救MongoDB管理员的GUI工具大赏:从菜鸟到极客的生存指南
-
作为一名在NoSQL丛林中披荆斩棘的数据猎人,没有比GUI工具更称手的瑞士军刀了。本文将带你围观五款主流MongoDB管理神器的特性与暗坑,附赠精准到扎心的吐槽指南一、MongoDBCompass:...
- mongodb/redis/neo4j 如何自己打造一个 web 数据库可视化客户端?
-
前言最近在做neo4j相关的同步处理,因为产线的可视化工具短暂不可用,发现写起来各种脚本非常麻烦。...
- solidworks使用心得,纯干货!建议大家收藏
-
SolidWorks常见问题...
- 统一规约-关乎数字化的真正实现(规范统一性)
-
尽管数字化转型的浪潮如此深入人心,但是,对于OPCUA和TSN的了解却又甚少,这难免让人质疑其可实现性,因为,如果缺乏统一的语义互操作规范,以及更为具有广泛适用的网络与通信,则数字化实际上几乎难以具...
- Elasticsearch节点角色配置详解(Node)
-
本篇文章将介绍如下内容:节点角色简介...
- 产前母婴用品分享 篇一:我的母婴购物清单及单品推荐
-
作者:DaisyH8746在张大妈上已经混迹很久了,有事没事看看“什么值得买”已渐渐成了一种生活习惯,然而却从来没有想过自己要写篇文章发布上来,直到由于我产前功课做得“太过认真”(认真到都有点过了,...
- 比任何人都光彩照人的假期!水润、紧致的肌肤护理程序
-
图片来源:谜尚愉快的假期临近了。身心振奋的休假季节。但是不能因为这种心情而失去珍贵的东西,那就是皮肤健康。炙热的阳光和强烈的紫外线是使我们皮肤老化的主犯。因此,如果怀着快乐的心情对皮肤置之不理,就会使...
- Arm发布Armv9边缘AI计算平台,支持运行超10亿参数端侧AI模型
-
中关村在线2月27日消息,Arm正式发布Armv9边缘人工智能(AI)计算平台。据悉,该平台以全新的ArmCortex-A320CPU和领先的边缘AI加速器ArmEthos-U85NPU为核心...
- 柔性——面向大规模定制生产的数字化实现的基本特征
-
大规模定制生产模式的核心是柔性,尤其是体现在其对定制的要求方面。既然是定制,并且是大规模的定制,对于制造系统的柔性以及借助于数字化手段实现的柔性,就提出了更高的要求。面向大规模定制生产的数字化业务管控...
- 创建PLC内部标准——企业前进的道路
-
作者:FrankBurger...
- 标准化编程之 ----------- 西门子LPMLV30测试总结
-
PackML乃是由OMAC开发且被ISA所采用的自动化标准TR88.00.02,能够更为便捷地传输与检索一致的机器数据。PackML的主要宗旨在于于整个工厂车间倡导通用的“外观和感觉”,...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
tinymce 号称富文本编辑器世界第一,大家同意么?
-
- 最近发表
-
- 5款Syslog集中系统日志常用工具对比推荐
- 跨平台、多数据库支持的开源数据库管理工具——DBeaver
- 强烈推荐!数据库管理工具:Navicat Premium 16.3.2 (64位)
- 3 年 Java 程序员还玩不转 MongoDB,网友:失望
- 拯救MongoDB管理员的GUI工具大赏:从菜鸟到极客的生存指南
- mongodb/redis/neo4j 如何自己打造一个 web 数据库可视化客户端?
- solidworks使用心得,纯干货!建议大家收藏
- 统一规约-关乎数字化的真正实现(规范统一性)
- Elasticsearch节点角色配置详解(Node)
- 产前母婴用品分享 篇一:我的母婴购物清单及单品推荐
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)